目录
前言
因工作需要,要实现一个小型的数据管理系统。本着想捡起来QT技能的想法,学习下QT开发界面管理数据库的操作。
一、QtSQL简介
QtSQL模块实现了对数据库的访问,Qt SQL提供了三个不同层次的API供开发者使用。包括驱动层,SQL API层,用户接口层。
层次 | 描述 |
---|---|
驱动层 | 实现了特定数据库与SQL接口的底层桥接,包括的支持类有QSqlDriver、QSqlDriverCreator、QSqlDriverCreatorBase、QSqlDriverPlugin和QSqlResult |
SQL API层 | QSqlDataBase类提供了数据库访问接口、数据库连接操作;QSqlQuery类提供了与数据库交互的操作,其他支持类还包括QSqlError、QSqlField、QSqlTableModel和QSqlRecord |
用户接口层 | 提供从数据库数据到用于数据表示的窗体的映射,包括的支持类有QSqlQueryModel、QSqlTableModel和QSqlRelationalTableModel,这些类均依据Qt的模型/视图结构设计 |
使用该模块需要以下两个步骤:
- 在项目文件即pro文件中添加如下行
QT += sql
- 在使用该模块的头文件中添加如下行
#include <QtSql>
二、SQLite简介
关系型数据库多种多样,常见的就是甲骨文的ORacle和微软的SQL Server,还有开源的MySQL。这些用在小型数据库管理上有点奢侈和臃肿。
Qt提供了一种进程内数据库SQLite。无需安装,轻量级的。也支持视图、触发器和事务,支持嵌套SQL功能。
三、QtSQL使用
3.1建立数据库连接
在进行数据库操作之前,必须首先建立与数据库的连接。数据库通信通常是由连接名称分辨而不是数据库名称。我们可以针对同一个数据库建立多个连接。QSqlDataBase类支持创建默认连接,它是没有名称的。如下是一段创建并打开默认连接的代码
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
db.setHostName("bigblue");
db.setDatabaseName("flightdb");
db.setUserName("acarlson");
db.setPassword("1uTbSbAs");
bool ok = db.open();
如上段代码所示,创建数据库连接仅仅是实例化了一个QSqlDatabase类的对象,想要使用它得调用open函数来打开它。静态函数addDatabase原型如下:
QSqlDatabase::addDatabase(
const QString &type,
const QString &connectionName = QLatin1String(defaultConnection)
)
参数type为驱动名,可以指定连接到哪些数据库;参数connectionname则为上述的连接名称,如未指定,则使用默认连接名,后续可直接使用不带参数的相关函数。QT支持的驱动如下:
Driver name | DBMS |
---|---|
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 |
打开数据库连接后我们就可以通过相关函数来操作数据库,在使用完毕后可通过下述两个函数来关闭移除数据库
QSqlDatabase::close()
QSqlDatabase::removeDatabase()
3.2执行SQL语句
QSqlQuery类提供了一个执行SQL语句的接口并且可以遍历执行的返回结果集。数据库连接建立后就可以使用该类执行底层数据库支持的SQL语句,此方法所要做的仅是创建一个该类实例对象,然后再调用该类的exec()函数。如下:
QSqlQuery query;
query.exec("SELECT name, salary FROM employee WHERE salary > 50000");
3.1.1遍历结果集
QSqlQuery类提供了一个获取记录的接口,在调用exec函数后,QSqlQuery类的内部指针指向第一条记录前的位置,我们必须调用一次QSqlQuery::next()函数来获取第一条记录的位置。然后再重复调用next函数,直到返回错误。
while (query.next()) {
QString name = query.value(0).toString();
int salary = query.value(1).toInt();
qDebug() << name << salary;
}
QSqlQuery::value()函数返回当前记录某个属性值。 Fields 是从0开始的索引。 我们可以通过 QSqlQuery::at()获取当前记录的行索引,通过QSqlQuery::size() 获取记录的总数,即行数。
QSqlQuery::value()的返回值是 QVariant类型, 该类型可以转换为多种 C++ and core Qt数据类型,如 int, QString, 和 QByteArray.不同的数据库类型自动映射到相近的Qt类型。
QSQLITE SQLite version 3 Data Types
QSQLITE SQLite version 3 data type | SQL type description | Recommended input (C++ or Qt data type) |
---|---|---|
NULL | NULL value. | NULL |
INTEGER | Signed integer, stored in 8, 16, 24, 32, 48, or 64-bits depending on the magnitude of the value. | typedef qint8/16/32/64 |
REAL | 64-bit floating point value. | By default mapping to QString |
TEXT | Character string (UTF-8, UTF-16BE or UTF-16-LE). | Mapped to QString |
CLOB | Character large string object | Mapped to QString |
BLOB | The value is a BLOB of data, stored exactly as it was input. | Mapped to QByteArray |
3.1.2增加、更新和删除记录
QSqlQuery可以执行多种SQL语句。不止select,如insert,update,delete。
1.insert
QSqlQuery query;
query.exec("INSERT INTO employee (id, name, salary) "
"VALUES (1001, 'Thad Beaumont', 65000)");
插入多个记录可以使用占位符操作,Qt支持两种占位符语法:有名占位和定位占位。分别为
有名占位即Oracle语法
QSqlQuery query;
query.prepare("INSERT INTO employee (id, name, salary) "
"VALUES (:id, :name, :salary)");
query.bindValue(":id", 1001);
query.bindValue(":name", "Thad Beaumont");
query.bindValue(":salary", 65000);
query.exec();
定位占位即ODBC语法
QSqlQuery query;
query.prepare("INSERT INTO employee (id, name, salary) "
"VALUES (?, ?, ?)");
query.addBindValue(1001);
query.addBindValue("Thad Beaumont");
query.addBindValue(65000);
query.exec();
2.update
QSqlQuery query;
query.exec("UPDATE employee SET salary = 70000 WHERE id = 1003");
3.delete
QSqlQuery query;
query.exec("DELETE FROM employee WHERE id = 1007");
3.1.3事务操作
事务可以用来确保复杂的操作是原子的(例如,查找外键并创建记录),或者提供一种消除中间复杂变化的方法。
可以使用QSQLDABASE::transaction()启动事务,然后是要在事务上下文中执行的SQL命令,最后是QSQLDABASE::commit()或QSQLDABASE::rollback()。
当使用事务操作时,必须在创捷查询前启动事务。举例如下:
QSqlDatabase::database().transaction();
QSqlQuery query;
query.exec("SELECT id FROM employee WHERE name = 'Torild Halvorsen'");
if (query.next()) {
int employeeId = query.value(0).toInt();
query.exec("INSERT INTO project (id, name, ownerid) "
"VALUES (201, 'Manhattan Project', "
+ QString::number(employeeId) + ')');
}
QSqlDatabase::database().commit();
3.3SQL 模型类
Qt同时也在用户接口层提供了三个模型类来对数据库进行操作,QSqlQueryModel、QSqlTableModel和QSqlRelationalTableModel。这三个类都继承自 QAbstractTableModel ,因此可以很方便的将数据通过QListView和QTableView展示在UI界面内;此外基于模型/视图结构,我们也可以很方便的在不改动代码的情况下更换数据源。
QSqlQueryModel
提供了一个基于SQL查询的只读模型
QSqlQueryModel model;
model.setQuery("SELECT * FROM employee");
for (int i = 0; i < model.rowCount(); ++i) {
int id = model.record(i).value("id").toInt();
QString name = model.record(i).value("name").toString();
qDebug() << id << name;
}
QSqlTableModel
提供了一个基于单张SQL表的可读写模型
QSqlTableModel model;
model.setTable("employee");
model.setFilter("salary > 50000");
model.setSort(2, Qt::DescendingOrder);
model.select();
for (int i = 0; i < model.rowCount(); ++i) {
QString name = model.record(i).value("name").toString();
int salary = model.record(i).value("salary").toInt();
qDebug() << name << salary;
}
使用该模型,可以替代写SQL语句来操作,如我们可以通过record()来或者表中的一行记录,通过setRecord()修改记录。在模型上的所有修改完成后,使用submitall或者submit来进行提交,更新数据库。
for (int i = 0; i < model.rowCount(); ++i) {
QSqlRecord record = model.record(i);
double salary = record.value("salary").toInt();
salary *= 1.1;
record.setValue("salary", salary);
model.setRecord(i, record);
}
model.submitAll();
设置编辑策略
编辑策略表示当用户在视图进行的数据修改是否完全应用到数据库。 取值范围有
常量 | 值 | 描述 |
---|---|---|
QSqlTableModel::OnFieldChange | 0 | 所有对模型的更改都会立即在数据库内更新 |
QSqlTableModel::OnRowChange | 1 | 当前行的修改在用户选择另一不同行时生效 |
QSqlTableModel::OnManualSubmit | 2 | 所有修改暂存在模型中,直到调用submitAll()或者revertAll() |
model->setEditStrategy(QSqlTableModel::OnManualSubmit);
QSqlRelationalTableModel
基于QSqlTableModel的扩展,支持外键。
3.4通过视图展示数据
QSqlQueryModel、QSqlTableModel和QSqlRelationalTableModel类均可以作为数据源在Qt的视图类中表示,如QListView、QTableView和QTreeView等视图类。其中,最常用的是QTableView,因该视图最适合表示二维的SQL操作结果。
如果模型是可读写的,视图将使用户可以编辑字段,我们可以通过如下调用禁止编辑。
view->setEditTriggers(QAbstractItemView::NoEditTriggers);
我们可以将一个模型用于多个视图,当在某一视图中更新编辑了模型后,其他视图将随即更新。
使用模型的插入行和删除行操作时,会在视图上进行特殊标注,直到进行提交。
视图类可以显示一个水平表头和一个垂直表头。水平表头在每一列之上显示一个列名,默认情况下,列名就是数据库表的字段名,可以通过setHeaderData()函数修改列名。垂直表头在每一行的最左侧显示本行的行号。
model->setHeaderData(0, Qt::Horizontal, QObject::tr("ID"));
model->setHeaderData(1, Qt::Horizontal, QObject::tr("Name"));
model->setHeaderData(2, Qt::Horizontal, QObject::tr("City"));
model->setHeaderData(3, Qt::Horizontal, QObject::tr("Country"));
创建数据感知表单
在某些时候,我们想通过一个表单来获取和更新数据库表中特定行或列的数据,这时候可以使用 QDataWidgetMapper类,具体可参考官方的BOOKS例子。暂时还没用上这个功能。