Qt-数据库开发-事务提交(3)

时间:2022-12-10 11:59:08

Qt-数据库开发-通过QSqlTableModel显示和修改数据,开启事务

更多精彩内容
????个人内容分类汇总 ????
????数据库开发 ????

1、概述

  • 这是通过学习Qt官方Demo产生的一个示例;
  • 通过自己理解加入了一些更加详细便于学习的内容;
  • 添加了非常详细的注释信息,对于小白更加友好。

开发环境说明

  • 系统:Windows10、Ubuntu20.04
  • Qt版本:V5.12.5
  • 编译器:MSVC2017-64、GCC/G++64

2、实现效果

  1. 通过QSqlTableModel将数据库内容直接显示到QTableView中,直观的显示和修改数据库中的数据;
  2. 程序启动时自动创建一个数据库,并创建一个表写入默认测试数据,可选择创建文件数据库还是内存数据库;
  3. 写入测试数据时演示了QSqlQuery的五种不同的插入数据方式;
  4. 在界面上修改内容后不会立即保存到数据库中,需要点击提交按键才一次性将所有修改的内容保存到数据库;
  5. 如果在界面上修改了内容,还没有提交事务时可以选择还原所有修改内容。
  • 实现效果如下:

    Qt-数据库开发-事务提交(3)

3、主要代码

  • 啥也不说了,直接上代码,一切有注释

  • pro文件: Qt使用到数据库,上来什么都别管,先在pro文件添加上QT += sql

  • main.cpp文件:程序第一次启动时在main函数中创建数据库并写入数据;

    #include "widget.h"
    
    #include <QApplication>
    #include <QSqlQuery>
    #include <qmessagebox.h>
    #include <qsqldatabase.h>
    
    /**
     * @brief  创建并打开一个QSqlite数据库,并创建一个测试表person,同时默认创建5组数据
     * @return
     */
    bool createConnection()
    {
        QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");  // 使用数据库驱动(Qsqlite)和默认连接名称(qt_sql_default_connection)添加一个数据库
    //    qDebug() << QSqlDatabase::defaultConnection;           // 打印默认数据库连接名称
    #if 1
        db.setDatabaseName("test.db");        // 使用文件数据库(可生成数据库文件,数据一直有效)
    #else
        db.setDatabaseName(":memory:");       // 使用内存数据库(不会生成数据库文件,所有数据都在内存中进行操作,性能强,程序退出后数据丢失)
    #endif
    
        if(!db.open())             // 打开数据库
        {
            QMessageBox::critical(nullptr, "Error", "打开数据库失败!");
            return false;
        }
    
        QSqlQuery query;          // 创建一个用于执行和操作Sql语句的对象
    
        // 创建一个表person,包含id、firstname、lastname三个字段
        query.exec("create table person ("
                   "id         int primary  key,"    // 索引
                   "firstname  varchar(20),"         // 名
                   "lastname   varchar(20))");       // 姓
    
        /****************** 向表中插入数据 ************************/
    
        // 插入方式一:直接插入数据
        query.exec("insert into person values(1, '悟空', '孙')");       // INSERT INTO 语法1(为表中所有项插入数据)
    
        // 插入方式二:使用命名占位符的[命名]绑定
        query.prepare("insert into person(id, firstname, lastname)"       // insert into 语法2
                      "values (:id, :firstname, :lastname)");
        query.bindValue(":id", 2);
        query.bindValue(":firstname", "悟净");
        query.bindValue(":lastname", "沙");
        query.exec();
    
        // 插入方式三:使用命名占位符的[位置]绑定
        query.prepare("insert into person(id, firstname, lastname)"       // insert into 语法2
                      "values (:id, :firstname, :lastname)");
        query.bindValue(0, 3);
        query.bindValue(1, "八戒");
        query.bindValue(2, "猪");
        query.exec();
    
        // 插入方式四:使用位置占位符绑定值(版本 1)
        query.prepare("insert into person(id, firstname, lastname)"       // insert into 语法2
                      "values (?, ?, ?)");
        query.bindValue(0, 4);
        query.bindValue(1, "白龙");
        query.bindValue(2, "小");
        query.exec();
    
        // 插入方式五:使用位置占位符绑定值(版本 2)
        query.prepare("insert into person(id, firstname, lastname)"       // insert into 语法2
                      "values (?, ?, ?)");
        query.addBindValue(5);                                           // 使用位置值绑定时,将值val添加到值列表中。addBindValue()调用的顺序决定了在准备好的查询中将值绑定到哪个占位符
        query.addBindValue("三藏");
        query.addBindValue("唐");
        query.exec();
    
        return true;
    }
    
    int main(int argc, char *argv[])
    {
        QApplication a(argc, argv);
    
        if(!createConnection()) return -1;    // 创建并打开一个数据库
    
        Widget w;
        w.show();
        return a.exec();
    }
    
    
  • widget.ui文件: 添加3个QPushButton用于提交和还原数据库事务、1个QTableView用于显示数据库内容;

    Qt-数据库开发-事务提交(3)

  • widget.h文件

    #ifndef WIDGET_H
    #define WIDGET_H
    
    #include <QWidget>
    class QSqlTableModel;
    
    QT_BEGIN_NAMESPACE
    namespace Ui { class Widget; }
    QT_END_NAMESPACE
    
    class Widget : public QWidget
    {
        Q_OBJECT
    
    public:
        Widget(QWidget *parent = nullptr);
        ~Widget();
    
    
    private slots:
        void on_but_submit_clicked();
    
        void on_but_revert_clicked();
    
        void on_but_quit_clicked();
    
    private:
        Ui::Widget *ui;
        QSqlTableModel* m_model = nullptr;
    };
    #endif // WIDGET_H
    
    
  • widget.cpp文件

    #include "widget.h"
    #include "ui_widget.h"
    
    #include <qmessagebox.h>
    #include <qsqltablemodel.h>
    #include <QSqlError>
    
    Widget::Widget(QWidget *parent)
        : QWidget(parent)
        , ui(new Ui::Widget)
    {
        ui->setupUi(this);
        this->setWindowTitle(QString("QSql-使用事务提交修改内容到数据库Demo - V%1").arg(APP_VERSION));
    
        m_model = new QSqlTableModel(this);
        m_model->setTable("person");         // 设置需要操作的表
        m_model->setEditStrategy(QSqlTableModel::OnManualSubmit);  // 设置数据不自动保存到数据库表
        m_model->select();                   // 查询表中所有数据
    
        ui->tableView->setModel(m_model);
        ui->tableView->resizeColumnsToContents();   // 根据表格中的内容自动调整列宽
    
    }
    
    Widget::~Widget()
    {
        delete ui;
    }
    
    /**
     * @brief 开启sqlite事务并提交修改,相对于直接使用submitAll更有优势(开始事务可以优化写入大量数据速度)
     */
    void Widget::on_but_submit_clicked()
    {
        bool ret = m_model->database().transaction();
        if(!ret) return;
        if(m_model->submitAll())           // 提交所有修改,如果开启了事务,则保存到事务
        {
            m_model->database().commit();  // 如果开启了事务,则将事务保存到数据库
            QMessageBox::about(this, "注意!", "保存修改内容成功!");
        }
        else
        {
            m_model->database().rollback();
            QMessageBox::warning(this, "错误!", QString("数据提交错误:%1").arg(m_model->lastError().text()));
        }
    }
    
    /**
     * @brief 还原所有未提交的修改
     */
    void Widget::on_but_revert_clicked()
    {
        m_model->revertAll();
    }
    
    /**
     * @brief 退出
     */
    void Widget::on_but_quit_clicked()
    {
        this->close();
    }
    

4、完整源代码