这篇总结一下Qt的数据库操作。这篇文章可能涉及一些数据库语句,我这里不做过多解释,如果您不太具备数据库知识,大概也可以看懂,实在不行,只好去补习下SQL的知识,起码把SQL语句学一下。
Qt 提供了 QtSql 模块来提供平台独立的基于 SQL 的数据库操作。这里我们所说的“平台独立”,既包括操作系统平台,又包括各个数据库平台。Qt 的数据库操作还可以很方便的与 model/view 架构进行整合。通常来说,我们对数据库的操作更多地在于对数据库表的操作,而这正是 model/view 架构的长项。
要在项目中启用Qt SQL,请将此伪指令添加到C ++文件中:
#include
要链接到Qt SQL模块,请将此行添加到项目文件中:
QT + = SQL
数据库相关的类有:
类名 |
描述 |
QSqlDatabase |
代表一个数据库链接 |
QSqlDriverCreator |
提供特定驱动程序类型的SQL驱动程序工厂的模板类 |
QSqlDriverCreatorBase |
QSqlDriverCreator的基类 |
QSqlDriver |
用于访问特定SQL数据库的抽象基类 |
QSqlError |
SQL数据库错误信息 |
QSqlField |
处理SQL数据库表和视图中的字段 |
QSqlIndex |
操纵和描述数据库索引的功能 |
QSqlQuery |
执行和操纵SQL语句的方法 |
QSqlRecord |
封装数据库记录 |
QSqlResult |
用于从特定SQL数据库访问数据的抽象接口 |
QSql |
包含在整个Qt SQL模块中使用的其他标识符 |
QSqlQueryModel |
SQL结果集的只读数据模型 |
QSqlRelationalTableModel |
具有外键支持的单个数据库表的可编辑数据模型 |
QSqlTableModel |
单个数据库表的可编辑数据模型 |
从上表中可以归纳出Qt SQL支持的三个层次数据库功能:
驱动层:
QSqlDriver,QSqlDriverCreator,QSqlDriverCreatorBase,QSqlDriverPlugin,QSqlResult.此层次为特定数据库和SQL API之间提供了低级的沟通桥梁.
SQL API层:
QSqlDatabase,QSqlQuery,QSqlError,QSqlField,QSqlIndex,QSqlRecord.此层此提供对数据库的访问.
用户界面层:
QSqlQueryModel, QSqlTableModel, QSqlRelationalTableModel.此层次将数据从数据库显示在widget上,要配合Qt的 model/view框架使用.
Qt SQL模块使用驱动程序插件(plugins)与不同的数据库API通信。由于Qt的SQL模块API与数据库无关,因此所有特定于数据库的代码都包含在这些驱动程序中。Qt本身提供了几个驱动程序,另外用户可以添加其他自定义驱动程序。为此,Qt提供了驱动程序源代码,可以将其用作编写自己的驱动程序的模型。
Qt本身已经支持的数据库驱动有:
驱动 |
数据库 |
QDB2 |
IBM DB2 (version 7.1 and above) |
QIBASE |
Borland InterBase |
QMYSQL |
MySQL |
QOCI |
Oracle Call Interface Driver |
QODBC |
Open Database Connectivity (ODBC) - Microsoft SQL Server and other ODBC-compliant databases |
QPSQL |
PostgreSQL (versions 7.3 and above) |
QSQLITE2 |
SQLite version 2 |
QSQLITE |
SQLite version 3 |
QTDS |
Sybase Adaptive Server;注意: 从Qt 4.7之后不支持 |
用户也可以自行编译需要的数据库驱动。曾在Qt4时代,我用Qt4.6自己编译过MySQL的驱动(Qt5.9已经默认支持MySQL了)。编译的过程都类似,大家可以移步到我的另一篇博客参观:编译MySQLQt4驱动.
其核心思想就是打开驱动源码,执行qmake和nmake:
cd %QTDIR%\qtbase\src\plugins\sqldrivers
qmake -- MYSQL_INCDIR=C:/MySQL/include "MYSQL_LIBDIR=C:/MYSQL/MySQL Server
nmake sub-mysql
对于Oracle数据库OCI,Qt5也需要自己编译其驱动。Qt OCI插件支持Oracle 9i,10g和更高版本。在Windows上构建OCI插件要首先从Oracle Client Installation CD中选择Oracle Client Installer中的“ Programmer”选项。对于某些版本的Oracle Client,您可能还需要选择“呼叫接口(OCI)”选项(如果有)。
cd %QTDIR%\qtbase\src\plugins\sqldrivers
qmake -- OCI_INCDIR=c:/oracle/oci/include OCI_LIBDIR=c:/oracle/oci/lib/msvc
nmake sub-oci
如果你没有使用Microsoft编译器,请在上面的行中替换nmake为mingw32-make。
如果你不确定自己的Qt支持了哪些驱动,可以用QSqlDatabase::drivers()找到系统中所有可用的数据库驱动的名字列表。
下面将具体以Qt操作MySQL为例总结Qt的数据库操作。
QSqlDatabase负责加载和管理数据库驱动程序插件。添加数据库后(请参见QSqlDatabase :: addDatabase()),将加载相应的驱动程序插件(使用QSqlDriverPlugin)。QSqlDatabase依靠驱动程序插件为QSqlDriver和QSqlResult提供接口。所以,首先需要添加数据库引擎:
QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL");
//其次就是设置数据库的地址、用户、密码等信息连接它:
db.setHostName("127.0.0.1"); //数据库服务器ip
db.setUserName("root"); //数据库用户名
db.setPassword("123456"); //密码
db.setDatabaseName("info"); //使用哪个数据库
//打开数据库
if(!db.open())
{
QMessageBox::warning(this, "错误", db.lastError().text());
return;
}
数据库的典型操作有增删改查。QSqlQuery类可以支持几乎所有的数据库语句。我们还可以通过使用QSqlQuery::isActive()函数检查语句执行正确与否。如果QSqlQuery对象是活动的,该函数返回 true。所谓“活动”,就是指该对象成功执行了exec()函数,但是还没有完成。如果需要设置为不活动的,可以使用finish()或者clear()函数,或者直接释放掉这个QSqlQuery对象。
QSqlQuery query(db);//绑定要访问的数据库
query.exec(" create table student(id int primary key auto_increment, name varchar(255), age int, score int);");
//插入单条数据
query.exec(" insert into student(id, name, age, score) values(1, 'mike', 18, 76);");
如果我们需要插入多条数据,此时可以使用QSqlQuery::exec()函数一条一条插入数据,但是这里我们选择了另外一种方法:批量执行。首先,我们使用QSqlQuery::prepare()函数对这条 SQL 语句进行占位符预处理,预示着以后我们可以使用实际数据替换这些位置。简单说明一下,预处理是数据库提供的一种特性,它会将 SQL 语句进行编译,性能和安全性都要优于普通的 SQL 处理。然后我们调用QSqlQuery::execBatch()批量执行 SQL,之后结束该对象。这样,插入操作便完成了。
//预处理语句:占位符: +自定义名字
query.prepare(" insert into student(name, age, score) values(:name, :age, :score);");
//给字段设置内容 list
QVariantList nameList,ageList, scoreList;
nameList << "小米" << "华为" << "三星";
ageList <<33 <<43 <<55;
scoreList << 59 << 69 << 100;
//给字段绑定(可以不按照顺序,有占位符: +自定义名字)
query.bindValue(":name",nameList);
query.bindValue(":age",ageList);
query.bindValue(":score",scoreList);
//执行预处理命令
query.execBatch();
查找数据即执行SQL语言中的select语句即可:
query.exec("select * from student where name = '华为'");//从表中遍历name为华为的行
while(query.next())//一行一行遍历
{
//取出当前行的内容,取得时候按列来取
qDebug() << query.value(0).toInt()
<< query.value(1).toString()
<< query.value("age").toInt()
<< query.value("score").toInt();
}
修改或叫更新数据用update语句:
query.exec("update student set score = 100 where name = '华为'");
删除用delete语句:
query.exec("delete from student where name = '华为'");
关闭数据库:
db.close();
总之,数据库的操作就是用exec执行SQL语句即可。至于SQL语句,可能不同的数据库还有些差别。在使用时要注意。
有的应用场景需要用到数据库的事物操作。主要包括:transaction(),commit()提交,rollback()回滚。注意:操作事务前,先判断该数据库是否支持事务操作。hasFeature是QSQLDriver类函数
//插入一条记录,然后提交事务
if (QSqlDatabase::database().driver()->hasFeature(QSqlDriver::Transactions))
{
...
}
//
QSqlDatabase::database().transaction();
QSqlQuery query;
query.exec("SELECT * FROM T_STUDENT WHERE age=12");
if (query.next())
{
query.exec("INSERT INTO T_STUDENT (name,age,score) VALUES ('小李',13,99)");
}
if(!QSqlDatabase::database().commit())
{
QMessageBox::critical(this,"Error","操作失败,将要回滚");
if(!QSqlDatabase::database().rollback())
{
QMessageBox::critical(this,"Error","回滚失败");
}
}
Qt SQL的用户界面层提供了几个非常方便使用的类,这些类将数据从数据库链接到数据感知小部件。它们包括QSqlQueryModel,QSqlTableModel和QSqlRelationalTableModel。这些类旨在与Qt的模型/视图框架一起使用。关于Qt的模型/视图架构,我已经在模型/视图里总结过了。
这种基于QSqlTableModel 的模型处理更为高级,如果对 SQL 语句不熟悉,并且不需要很多复杂的查询,这种QSqlTableModel模型基本可以满足一般的需求。值得注意的是,QSqlTableModel并不一定非得结合 QListView或QTableView使用,我们完全可以用其作一般性处理。
连接数据库的操作依然与上面的介绍的一样,当连接成功后,我们可以通过QSqlTableModel来进行数据库操作。
model->setTable ("student");//设定表名
model->setEditStrategy (QSqlTableModel::OnManualSubmit);//所有的操作都将被缓存,直到submitAll() or revertAll()被调用.
model->select ();//选取整个表的所有行
ui->tableView->setModel (model);
使用setTable()函数设置所需要操作的表,用select()选择整个表,在用view的setModel接口即可将整个数据表显示在UI的表格中。如果想查询或显示部分,只要设置过滤器就可以了:
model.setFilter("age > 20 and age < 25");
也就是 WHERE 语句所需要的部分。例如上面代码中的操作实际相当于 SQL 语句
SELECT * FROM student WHERE age > 20 and age < 25
我们也可以通过下面的方式遍历数据:
if (model.select())
{
for (int i = 0; i < model.rowCount(); ++i)
{
QSqlRecord record = model.record(i);
QString name = record.value("name").toString();
int age = record.value("age").toInt();
qDebug() << name << ": " << age;
}
}
使用QSqlTableModel::select()函数进行操作,也就是执行了查询操作。如果查询成功,函数返回 true,由此判断是否发生了错误。如果没有错误,我们使用record()函数取出一行记录,该记录是以QSqlRecord的形式给出的,而QSqlRecord::value()则取出一个列的实际数据值。注意,由于QSqlTableModel没有提供const_iterator遍历器,因此不能使用foreach宏进行遍历。
另外需要注意,由于QSqlTableModel只是一种高级操作,肯定没有实际 SQL 语句方便。具体来说,我们使用QSqlTableModel只能进行 SELECT * 的查询,不能只查询其中某些列的数据。
下面一段代码则显示了如何使用QSqlTableModel进行插入操作:
QSqlTableModel model;
model.setTable("student");
int row = 0;
model.insertRows(row, 1);
model.setData(model.index(row, 1), "Cheng");
model.setData(model.index(row, 2), 24);
model.setData(model.index(row, 3), 99);
model.submitAll();
插入也很简单:model.insertRows(row, 1);说明我们想在索引 0 的位置插入 1 行新的数据。使用setData()函数则开始准备实际需要插入的数据。注意这里我们向 row 的第一个位置写入 Cheng(通过model.index(row, 1),回忆一下,我们把 model 当作一个二维表,这个坐标相当于第 row 行第 1 列),其余以此类推。最后,调用submitAll()函数提交所有修改。这里执行的操作可以用如下 SQL 表示:
INSERT INTO student (name, age,score) VALUES ('Cheng', 24,99)
当我们取出了已经存在的数据后,对其进行修改,然后重新写入数据库,即完成了一次更新操作:
record.setValue("age", 26);
model.setRecord(0, record);
model.submitAll();
//或者:
model.setData(model.index(0, 1), 26);
注意我们的 age 列是第2列,索引值为 1,这里的更新操作则可以用如下 SQL 表示:
UPDATE student SET age = 26 WHERE age = 25
删除操作如下:
model.removeRows(0, 1);
model.submitAll();
这相当于:
DELETE FROM student WHERE age = 25
另外,model中还提供了排序功能:
model->setSort (0, Qt::AscendingOrder);
model->select ();
事物操作:
model->database ().transaction (); //开始事物操作
if(model->submitAll ())
model->database ().commit ();//提交
else
{
model->database ().rollback ();//回滚
QMessageBox::warning (this, tr("tableModel"), tr("数据库错误:%1").arg (model->lastError ().text ()));
}
撤销:
model->revertAll ();//撤销修改
最后放两个例子,一是用数据库语言实现的,另一个则用模型/视图框架实现:例子