一、安装SQLite

        在Qt6中,不再支持 SQLite2,只支持 SQLite3 。因此,有两种方式使用 SQLite,一种是在 SQLite 官网安装 SQLite3,另外一种是直接安装 SQLite Expert。

        SQLite Expert 内置了 SQLite。这意味着用户在安装 SQLite Expert 后,无需单独安装 SQLite 即可使用其全部功能。内置的 SQLite 引擎使得用户能够直接创建、管理和操作 SQLite 数据库,而不必担心额外的配置和兼容性问题。点击如下链接即可安装:

SQLite administration | SQLite Expert

二、连接SQL

1. 修改项目配置文件(.pro文件)

        如果需要在Qt项目中使用数据库编程功能,需要将Qt SQL模块添加到项目中,也就是需要在项目配置文件中增加下面一句:

QT += sql

 2. 修改头文件(.h文件)

        如果要使用一些常用的Qt SQL类,在头文件中则需要添加如下代码:

#include <QtSql>

3. 添加数据库连接(.cpp文件)

        QSqlDatabase 类用于建立与数据库的连接,而 QSqlDatabase 对象就表示这种连接。例如接下来说的 QSqlQuerryModel 模型类查询数据库时,就必须设置它。它的函数原型定义如下:

QSqlDatabase QSqlDatabase::addDatabase(const QString &type, const QString &connectionName = QLatinString(defaultConnection))
  •  type:连接的数据库类型;
  • connectionName:所创建的数据库连接名称,若不设置则使用默认的数据库连接名称"qt_default_connection"

举个例子: 

//打开数据库
void MainWindow::on_actOpenDB_triggered()
{
    QString aFile = QFileDialog::getOpenFileName(this,"选择文件","","SQLite数据库(*.db3)");
    if (aFile.isEmpty())
        return;

    QSqlDatabase DB = QSqlDatabase::addDatabase("QSQLITE");  //添加 SQLITE数据库驱动
    DB.setDatabaseName(aFile);   //设置数据库文件
    if (DB.open())       //成功打开数据库
        selectData();  // 展示数据库数据
    else
        QMessageBox::warning(this, "错误", "打开数据库失败");
}

         这段代码中,当在打开的文件对话框中选中一个 .db3 文件并打开后,就成功地创建了一个SQLite数据库连接。

三、关于Qt SQL的模型类

        了解这些内容之前,我们必须知道各个模型类的继承关系。

1. QSqlQuerryModel

        这是一个表示SQL查询结果数据的模型类,它通过设置select语句查询数据库的数据。因此,这个模型类的数据是只读的,不可修改

        要想将数据库中查询到的数据作为 QSqlQuerryModel 的数据源,就必须使用 setQuery() 函数,其函数定义如下:

void QSqlQueryModel::setQuery(const QString &query, const QSqlDatabase &db = QSqlDatabase())
  • query:一条完整的select语句,用来查询数据库;
  • db:数据库连接 ,若不指定,则使用程序默认数据库连接

举个例子: 

//查询数据
void MainWindow::selectData()
{
    QSqlQueryModel qryModel = new QSqlQueryModel(this);
    qryModel->setQuery("SELECT empNo, Name, Gender,  Birthday,  Province,  Department, Salary FROM employee ORDER BY empNo", DB);
    ui->tableView->setModel(qryModel);
}

         这样在视图中就可通过 QSqlQuerryModel 模型显示数据库中的数据。

2. QSqlTableModel

        表示单个数据表的模型类。直接设置一个数据表的名称,就可以获取数据表的全部记录。与QSqlQuerryModel 不同的是,它的数据是可编辑的。其函数定义如下:

QSqlTableModel (QObject *parent = nullptr, const QSqlDatabase &db = QSqlDatabase())

举个例子:

// 打开数据表
void MainWindow::openTable()
{
    QSqlTableModel tabModel = new QSqlTableModel(this, DB);	
    tabModel->setTable("employee");			//选择一个数据表作为数据源
    tabModel->setEditStrategy(QSqlTableModel::OnManualSubmit);	 //设置为临时保存策略
    if (!(tabModel->select())) {	//查询数据失败
        QMessageBox::critical(this, "错误信息", "打开数据表错误,错误信息:\n" + tabModel->lastError().text());
        return;
    }
    ui->tableView->setModel(tabModel);
}

// 保存修改
void MainWindow::on_actSubmit_triggered()
{
    tabModel->submitAll();
}

// 取消修改
void MainWindow::on_actRevert_triggered()
{
    tabModel->revertAll();
}

        与 QSqlQuerryModel 不同的是,我们无须使用select语句直接使用内置的 select() 方法即可。而且当在tableView视图中修改了某项数据后,可以通过 submitAll() 将数据的修改提交到数据库。

3. QSqlRelationalTableModel

        它可以作为具有编码字段的数据表的模型类。这是什么意思?

        如上面三张表,我们希望将第一张表的 majorID 字段与第二章表的 majorID 字段连接,然后将第一张表的majorID“编码”为第二张表的major。同理第三张表也是如此。

        那么这种操作就需要用到 QSqlRelationalTableModel ,其函数定义如下:

QSqlRelationalTableModel (QObject *parent = nullptr, const QSqlDatabase &db = QSqlDatabase())

        如何设置上述的SQL的外键关系?主要用到一个函数 setRelation() :

void QSqlRelationalTableModel::setRelation(int column, const QSqlRelation &relation)
QSqlRelation(const QString &tableName, const QString &indexColumn, const QString &displayColumn)
  • column:外键字段的字段序号;
  • relation:一个 QSqlRelation 对象
    • tableName:编码表表名
    • indexColumn:外键字段名
    • displayColumn:编码表中代码含义字段名称

 举个例子:

// 打开数据表
void MainWindow::openTable()
{
    QSqlRelationalTableModel tabModel = new QSqlRelationalTableModel(this, DB);
    tabModel->setTable("studInfo");     
    tabModel->setEditStrategy(QSqlTableModel::OnManualSubmit); 
    ui->tableView->setModel(tabModel);

    //设置外键关系--QSqlRelation("另一个表名","另一个表里的外键","另一个表里的字段名称"))
    tabModel->setRelation(tabModel->fieldIndex("departID"), QSqlRelation("departments","departID","department"));  //学院
    tabModel->setRelation(tabModel->fieldIndex("majorID"), QSqlRelation("majors","majorID","major"));            //专业

    //为关系型字段设置默认代理组件
    ui->tableView->setItemDelegate(new QSqlRelationalDelegate(ui->tableView));  
}

        这样,当我们用tableView显示上述数据库表一内容时,就会自动编码majorID和departID。而且QSqlRelationalTableModel的默认代理组件也会对单元格进行委托代理,实现类似QComboBox样式的编辑效果。

四、QSqlQuery类

         在上述的模型类当中,我们只能双击单元格修改、再点击提交的方式修改数据库中的数据。这样显得很呆!而 QSqlQuery 是能运行任何 SQL 语句的类,如select、insert、update、delete等常用SQL语句。

        但是,QSqlQuery 并没有父类,与上述模型类没有直接继承关系,因此查询到的数据并不能作为 模型/视图 结构中的数据模型,只能作为一个数据集使用。

        几个重要的接口函数如下:

bool prepare()     //设置准备运行的SQL语句
void bindValue()     // 绑定prepare()中的SQL语句含有的参数
void exec()     //运行设置好的SQL语句
QString executedQuery()     //返回上一次成功执行的SQL语句

        注:若 prepare() 函数中没有参数,可以直接在 exec()  函数中放置SQL语句直接执行。

        举个例子:

// tableView上双击,编辑当前记录
void MainWindow::on_tableView_doubleClicked(const QModelIndex &index)
{
    int curRecNo = index.row();
    updateRecord(curRecNo);
}

// 更新一条记录
void MainWindow::updateRecord(int recNo)
{
    QSqlRecord curRec = qryModel->record(recNo);  //获取数据模型的一条记录
    int empNo = curRec.value("EmpNo").toInt();    //获取员工工号这个主键

    QSqlQuery query;
    query.prepare("select * from employee where EmpNo = :ID");
    query.bindValue(":ID",empNo);
    query.exec();
    if (!query.isValid())
        return;

    TDialogData *dataDialog = new TDialogData(this); //创建对话框
    if (dataDialog->exec() == QDialog::Accepted)
    {
        QSqlRecord recData = dataDialog->getRecordData();    //获得对话框返回的记录
        query.prepare("update employee set Name=:Name, Gender=:Gender,"
                      " Birthday=:Birthday,  Province=:Province,"
                      " Department=:Department, Salary=:Salary,"
                      " Memo=:Memo, Photo=:Photo "
                      " where EmpNo = :ID");
        query.bindValue(":Name", recData.value("Name"));
        query.bindValue(":Gender", recData.value("Gender"));
        query.bindValue(":Birthday", recData.value("Birthday"));
        query.bindValue(":Province", recData.value("Province"));
        query.bindValue(":Department", recData.value("Department"));
        query.bindValue(":Salary", recData.value("Salary"));
        query.bindValue(":Memo", recData.value("Memo"));
        query.bindValue(":Photo", recData.value("Photo"));
        query.bindValue(":ID", empNo);
        if (!query.exec())
            QMessageBox::critical(this, "错误", "记录更新错误\n" + query.lastError().text());
        else
            //数据模型重新查询数据,更新tableView显示
            qryModel->setQuery(qryModel->query().executedQuery());
    }
    delete dataDialog;      //删除对话框
}

        注意:在重新查询数据时,下列写法是错误的: 

//query.executedQuery()返回上一条成功执行的SQL语句
qryModel->setQuery(query.executedQuery());

        因为query对象为QSqlQuery类,它的上一条成功执行的SQL语句不一定是select子句。而qryModel对象为QSqlQueryModel类,它只能执行select子句,否则会导致编译错误。

        当我们再次双击单元格想要修改某一项数据时,就会打开一个 “更新记录” 的对话框。对话框将获取到的信息作为 bindValue() 函数的参数,最后用 exec() 执行后,数据库中的数据就会改变。当然,想要界面上的信息也发生改变,数据模型必须重新查询数据库中的数据。

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐