缓存SQL表

The Cached Table example shows how a table view can be used to access a database, caching any changes to the data until the user explicitly submits them using a push button.

本例由一个类组成,TableEditor,它是一个自定义对话框小部件,允许用户修改数据库中存储的数据。我们首先将回顾类定义和如何使用该类,然后我们将查看实现。

TableEditor 类定义

TableEditor类继承自<(QtWidget.html)QWidget,使表格编辑小部件成为一个顶层对话框窗口。

class TableEditor : public QWidget
{
    Q_OBJECT

public:
    explicit TableEditor(const QString &tableName, QWidget *parent = nullptr);

private slots:
    void submit();

private:
    QPushButton *submitButton;
    QPushButton *revertButton;
    QPushButton *quitButton;
    QDialogButtonBox *buttonBox;
    QSqlTableModel *model;
};

TableEditor 构造函数接受两个参数:第一个是TableEditor对象将操作的数据库表的引用。第二个是一个指向父小部件的指针,并将其传递给基类构造函数。

注意QSqlTableModel变量声明:正如下面示例所示,QSqlTableModel类可用于向视图类,例如(QTableView.html)QTableView提供数据。该类提供了一个可编辑的数据模型,使得能够从单个表读取和写入数据库记录。它是在更底层的(QSqlQuery.html)QSqlQuery类之上构建,该类提供了执行和操作SQL语句的手段。

我们还将展示如何使用表格视图来缓存对数据的任何变更,直到用户明确请求提交它们。因此,除了模型和编辑器的按钮外,我们还需要声明一个submit()槽。

连接到数据库
在我们可以使用TableEditor类之前,我们必须创建一个连接到我们想要编辑的表的数据库
int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    if (!createConnection())
        return 1;

    TableEditor editor("person");
    editor.show();
    return app.exec();
}

createConnection()函数是一个便捷的辅助函数。它在connection.h文件中定义,位于(Q示例目录(sql))中 (所有(Q SQL目录中)的示例都使用此函数连接到数据库)。

static bool createConnection()
{
    QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
    db.setDatabaseName(":memory:");
    if (!db.open()) {
        QMessageBox::critical(nullptr, QObject::tr("Cannot open database"),
            QObject::tr("Unable to establish a database connection.\n"
                        "This example needs SQLite support. Please read "
                        "the Qt SQL driver documentation for information how "
                        "to build it.\n\n"
                        "Click Cancel to exit."), QMessageBox::Cancel);
        return false;
    }

    QSqlQuery query;
    query.exec("create table person (id int primary key, "
               "firstname varchar(20), lastname varchar(20))");
    query.exec("insert into person values(101, 'Danny', 'Young')");
    query.exec("insert into person values(102, 'Christine', 'Holand')");
    query.exec("insert into person values(103, 'Lars', 'Gordon')");
    query.exec("insert into person values(104, 'Roberto', 'Robitaille')");
    query.exec("insert into person values(105, 'Maria', 'Papadopoulos')");

    query.exec("create table items (id int primary key,"
                                             "imagefile int,"
                                             "itemtype varchar(20),"
                                             "description varchar(100))");
    query.exec("insert into items "
               "values(0, 0, 'Qt',"
               "'Qt is a full development framework with tools designed to "
               "streamline the creation of stunning applications and  "
               "amazing user interfaces for desktop, embedded and mobile "
               "platforms.')");
    query.exec("insert into items "
               "values(1, 1, 'Qt Quick',"
               "'Qt Quick is a collection of techniques designed to help "
               "developers create intuitive, modern-looking, and fluid "
               "user interfaces using a CSS & JavaScript like language.')");
    query.exec("insert into items "
               "values(2, 2, 'Qt Creator',"
               "'Qt Creator is a powerful cross-platform integrated "
               "development environment (IDE), including UI design tools "
               "and on-device debugging.')");
    query.exec("insert into items "
               "values(3, 3, 'Qt Project',"
               "'The Qt Project governs the open source development of Qt, "
               "allowing anyone wanting to contribute to join the effort "
               "through a meritocratic structure of approvers and "
               "maintainers.')");

    query.exec("create table images (itemid int, file varchar(20))");
    query.exec("insert into images values(0, 'images/qt-logo.png')");
    query.exec("insert into images values(1, 'images/qt-quick.png')");
    query.exec("insert into images values(2, 'images/qt-creator.png')");
    query.exec("insert into images values(3, 'images/qt-project.png')");

    return true;
}

createConnection函数打开一个到内存中的SQLITE数据库的连接并创建一个测试表。如果您想使用其他数据库,只需修改此函数的代码。

TableEditor Class Implementation

类实现由仅包含两个函数组成,构造函数和submit()槽。在构造函数中,我们创建并自定义数据模型和窗口的各种元素。

TableEditor::TableEditor(const QString &tableName, QWidget *parent)
    : QWidget(parent)
{
    model = new QSqlTableModel(this);
    model->setTable(tableName);
    model->setEditStrategy(QSqlTableModel::OnManualSubmit);
    model->select();

    model->setHeaderData(0, Qt::Horizontal, tr("ID"));
    model->setHeaderData(1, Qt::Horizontal, tr("First name"));
    model->setHeaderData(2, Qt::Horizontal, tr("Last name"));

首先,我们创建数据模型并设置SQL数据库表,模型将要操作这个表。注意,QSqlTableModel::setTable() 函数并不会从表中选择数据;它只获取字段信息。因此,我们稍后调用 QSqlTableModel::select() 函数,用表中的数据填充模型。可以选择个性化设置,通过指定过滤器和排序条件(有关更多详细信息,请参阅 QSqlTableModel 类文档)。

我们还设置了模型的编辑策略。编辑策略决定了用户在视图中所做的更改何时实际应用到数据库中。由于我们想要缓存在表格视图中(即在模型中)的更改,直到用户明确提交它们,所以我们选择了 QSqlTableModel::OnManualSubmit 策略。其他选择包括 QSqlTableModel::OnFieldChangeQSqlTableModel::OnRowChange

最后,我们使用模型从 QSqlQueryModel 类继承的 setHeaderData() 函数来设置在视图标题中显示的标签。

    QTableView *view = new QTableView;
    view->setModel(model);
    view->resizeColumnsToContents();

然后我们创建一个表格视图。QTableView 类提供了一个默认的模型/视图实现,即它实现了能够从模型中显示项的表格视图。它还允许用户编辑项,并将更改存储在模型中。为了创建只读视图,您可以使用视图从 QAbstractItemView 类继承的 editTriggers 属性设置适当的标志。

为了使视图呈现我们的数据,我们使用 setModel() 函数将模型传递给视图。

    submitButton = new QPushButton(tr("Submit"));
    submitButton->setDefault(true);
    revertButton = new QPushButton(tr("&Revert"));
    quitButton = new QPushButton(tr("Quit"));

    buttonBox = new QDialogButtonBox(Qt::Vertical);
    buttonBox->addButton(submitButton, QDialogButtonBox::ActionRole);
    buttonBox->addButton(revertButton, QDialogButtonBox::ActionRole);
    buttonBox->addButton(quitButton, QDialogButtonBox::RejectRole);

TableEditor 的按钮是常规的 QPushButton 对象。我们将它们添加到按钮框中,以确保按钮按照当前小部件样式呈现合适的布局。这样做的原因是,对话框和消息框通常按照该平台界面准则呈现按钮。不同平台对对话框的布局各不相同。QDialogButtonBox 允许开发者向其添加按钮,并会自动使用用户桌面环境中的相应布局。

大多数对话框的按钮遵循某些角色。当使用 addButton() 函数将按钮添加到按钮框时,必须使用 QDialogButtonBox::ButtonRole 枚举指定按钮的角色。或者,QDialogButtonBox 提供了几个标准按钮(例如 OKCancelSave),您可以使用它们。它们作为标志存在,所以在构造函数中可以将它们按位或。

    connect(submitButton, &QPushButton::clicked, this, &TableEditor::submit);
    connect(revertButton, &QPushButton::clicked,  model, &QSqlTableModel::revertAll);
    connect(quitButton, &QPushButton::clicked, this, &TableEditor::close);

我们将 Quit 按钮连接到表格编辑器的 close() 插槽,将 Submit 按钮连接到我们的私有 submit() 插槽。后者槽将处理数据事务。最后,我们将 Revert 按钮连接到我们的模型的 revertAll() 插槽,撤消所有挂起更改(即恢复原始数据)。

    QHBoxLayout *mainLayout = new QHBoxLayout;
    mainLayout->addWidget(view);
    mainLayout->addWidget(buttonBox);
    setLayout(mainLayout);

    setWindowTitle(tr("Cached Table"));
}

最终,我们将按钮框和表格视图添加到布局中,在表格编辑器小部件上安装布局,并设置编辑器的窗口标题。

void TableEditor::submit()
{
    model->database().transaction();
    if (model->submitAll()) {
        model->database().commit();
    } else {
        model->database().rollback();
        QMessageBox::warning(this, tr("Cached Table"),
                             tr("The database reported an error: %1")
                             .arg(model->lastError().text()));
    }
}

submit() 槽在用户点击 Submit 按钮以保存更改时被调用。

首先,我们使用QSqlDatabase::transaction()函数在数据库上开始一个事务。数据库事务是与数据库管理系统或类似系统交互的单位,其处理方式独立于其他事务,且可以连贯和可靠。可以使用QSqlTableModel::database()函数获取所使用数据库的指针。

然后,我们尝试提交所有挂起的更改,即模型中修改的项目。如果没有错误发生,我们使用QSqlDatabase::commit()函数将事务提交到数据库(注意:在某些数据库中,如果数据库上存在活动的QSqlQuery,则此函数可能不起作用)。否则,我们使用QSqlDatabase::rollback()函数回滚事务并向用户发出警告。

参见

Qt的SQL数据库类的完整列表,以及模型/视图编程文档。

示例项目 @ code.qt.io

© 2024 Qt公司有限公司。本文件中包含的文档贡献归其各自所有者所有。所提供的文档是根据自由软件基金会发布的GNU自由文档许可证版本1.3的条款许可的。Qt及其相关徽标是世界各地芬兰和/或其他国家Qt公司有限公司的商标。所有其他商标归其各自所有者所有。