Qt提供的QtSql模块实现了对数据库的访问,同时提供了一套与平台和具体所用数据库均无关的调用接口。此模块为不同层次的用户提供了不同的丰富的数据库操作类。例如,对于习惯使用SQL语法的用户,QSqlQuery类提供了直接执行任意SQL语句并处理返回结果的方法;而对于习惯使用较高层数据库接口以避免使用SQL语句的用户,QSqlTableModel和QSqlRelationalTableModel类则提供了合适的抽象。
除此之外,此模型还支持常用的数据库模式,如主从视图(master-detail views)和向下钻取(drill-down)模式。
这个模块由不同Qt类支撑的三部分组成,QtSql模块层次结构见下表。
QtSql模块层次结构
层 次 | 描 述 |
驱动层 | 实现了特定数据库与SQL接口的底层桥接,包括的支持类有QSqlDriver、QSqlDriverCreator<T>、QSqlDriverCreatorBase、QSqlDriverPlugin和QSqlResult |
SQL接口层 | QSqlDatabase类提供了数据库访问、数据库连接操作,QSqlQuery类提供了与数据库的交互操作,其他支持类有QSqlError、QSqlField、QSqlTableModel和QSqlRecord |
用户接口层 | 提供从数据库数据到用于数据表示的窗体的映射,包括的支持类有QSqlQueryModel、QSqlTableModel和QSqlRelationalTableModel,这些类均依据Qt的模型概图结构设计 |
项目中通常采用各种数据库(如Oracle、SQLServer、MySQL等)来实现对数据的存储、检索等功能。这些数据库除提供基本的查询、删除和添加等功能外,还提供很多高级特性,如触发器、存储过程、数据备份恢复和全文检索功能等。但实际上,很多应用仅利用了这些数据库的基本特性,而且在某些特殊场合的应用中,这些数据库明显有些臃肿。
Qt提供了一种进程内数据库SQLite。它小巧灵活,无须额外安装配置且支持大部分ANSI SQL-92标准,是一个轻量级的数据库,概括起来具有以下优点。
在持久存储的情况下,一个完整的数据库对应于磁盘上的一个文件,它是一种具备基本数据库特性的数据文件,同一个数据文件可以在不同机器上使用,可以在不同字节序的机器间自由共享;最大支持2TB数据容量,而且性能仅受限于系统的可用内存;没有其他依赖,可以应用于多种操作系统平台。
基于控制台的程序,使用SQLite数据库完成大批量数据的增加、删除、更新和査询操作并输出。
操作步骤如下。
(1) 在“QSQLiteEx.pro”文件中添加如下语句:
QT += sqlQT += core
(2) 源文件“main.cpp”的具体代码如下:
#include <QCoreApplication>#include <QTextCodec>#include <QSqlDatabase>#include <QSqlQuery>#include <QTime>#include <QSqlError>#include <QtDebug>#include <QSqlDriver>#include <QSqlRecord>int main(int argc, char *argv[]){ QCoreApplication a(argc, argv); QTextCodec::setCodecForLocale(QTextCodec::codecForLocale()); //设置中文显示 QSqlDatabase db=QSqlDatabase::addDatabase("QSQLITE"); // (a) db.setHostName("myNoteBook"); //设置数据库主机名 db.setDatabaseName("qtDB.db"); // (b) db.setUserName("casaries"); //设置数据库用户名 db.setPassword("123456"); // 设置数据库密码 db.open(); // 打开连接 //创建数据库表 QSqlQuery query; //(c) bool success = query.exec(QString("create table automobile " "(id int primary key," "attribute varchar," "type varchar," "kind varchar," "nation int," "carnumBer int," "elevaltor int," "distance int," "oil int," "temperature int)")); // (d) if(success) qDebug()<<QObject::tr("数据库表创建成功!\n"); else qDebug()<<QObject::tr("数据库表创建失败!\n"); //查询 query.exec("select * from automobile"); QSqlRecord rec = query.record(); qDebug()<<QObject::tr("automobile 表字段数:")<<rec.count(); //插入记录 QTime t = QTime::currentTime(); //创建一个计时器,统计操作耗时 query.prepare("insert into automobile values(?,?,?,?,?,?,?,?,?,?)"); // (e) long records=100; // 向表中插入任意的100条记录 for(int i=0;i<records;i++){ query.bindValue(0,i); // (f) query.bindValue(1,"四轮"); query.bindValue(2,"轿车"); query.bindValue(3,"富康"); query.bindValue(4,rand()%100); query.bindValue(5,rand()%10000); query.bindValue(6,rand()%300); query.bindValue(7,rand()%200000); query.bindValue(8,rand()%52); query.bindValue(9,rand()%100); success=query.exec(); // (g) if(!success){ QSqlError lastError=query.lastError(); qDebug()<<lastError.driverText()<<QString(QObject::tr("插入失败")); } } QTime curtime = QTime::currentTime(); qDebug()<<QObject::tr("插入 %1 条记录,耗时:%2 ms").arg(records).arg(0-curtime.msecsTo(t)); //(h) //排序 t = curtime; //重新开始计时 success = query.exec ("select * from automobile order by id desc"); //(i) curtime = QTime::currentTime(); if (success) qDebug()<<QObject::tr("排序 %1 条记录,耗时:%2 ms").arg(records).arg(0-curtime.msecsTo(t));//输出操作耗时 else qDebug()<<QObject::tr("排序失败! "); //更新记录 t = curtime; //重新开始计时 for (int i = 0;i < records;i++){ query.clear(); query.prepare(QString("update automobile set " "attribute=?,type=?," "kind=?,nation=?," "carnumber=?,elevaltor=?," "distance=?,oil=?," "temperature=? where id=%1").arg(i)); //(j) query.bindValue(0,"四轮"); query.bindValue(1,"轿车"); query.bindValue(2,"富康"); query.bindValue(3,rand()%100); query.bindValue(4,rand()%10000); query.bindValue(5,rand()%300); query.bindValue(6,rand()%200000); query.bindValue(7,rand()%52); query.bindValue(8,rand()%100); success = query.exec(); if(!success){ QSqlError lastError = query.lastError(); qDebug()<<lastError.driverText()<<QString(QObject::tr("更新失败")); } } curtime = QTime::currentTime(); qDebug()<<QObject::tr("更新 %1 条记录,耗时:%2 ms").arg(records).arg(0 - curtime.msecsTo(t)); //删除 t = curtime; //重新开始计时 query.exec("delete from automobile where id=15");//(k) curtime = QTime::currentTime(); //输出操作耗时 qDebug()<<QObject::tr("删除一条记录,耗时:%1 ms").arg (0 - curtime.msecsTo(t)); return 0;}
其中,
① 在进行数据库操作之前,必须首先建立与数据库的连接。数据库连接由任意字符串标识。在没有指定连接的情况下,QSqlDatabase可以提供默认连接供Qt其他的SQL类使用。建立一条数据库连接的代码如下:
QSqlDatabase db=QSqlDatabase::addDatabase("QSQLITE"); // (a)db.setHostName("myNoteBook"); //设置数据库主机名db.setDatabaseName("qtDB.db"); // (b)db.setUserName("FutureRaider"); //设置数据库用户名db.setPassword("123456"); // 设置数据库密码db.open(); // 打开连接
其中,静态函数QSqlDatabase::addDatabase()返回一条新建立的数据库连接,其原型为:
QSqlDatabase QSqlDatabase::addDatabase(const QString &type, const QString &connectionName = QLatin1String(defaultConnection))
*参数type为驱动名,本例使用的是QSQLITE驱动。
*参数connectionName为连接名,默认值为默认连接。如果没有指定此参数,则新建立的数据库连接将成为本程序的默认连接,并且可以被后续不带参数的函数database()引用。如果指定了此参数(连接名),则函数database(connectionName)将获取这个指定的数据库连接。
② QtSql模块使用驱动插件(driver plugins)与不同的数据库接口通信。由于QtSql模块的应用程序接口是与具体数据库无关的,所以所有与数据库相关的代码均包含在这些驱动插件中。目前,Qt支持的数据库驱动插件见下表。由于版权的限制,开源版Qt不提供上述全部驱动,所以配置Qt时,可以选择将SQL驱动内置于Qt中或编译成插件。如果Qt中支持的驱动不能满足要求,还可以参照Qt的源代码编写数据库驱动。
Qt支持的数据库驱动插件
驱 动 | |
QDB2 | IBM DB2及其以上版本 |
QIBASE | Borland InterBase |
QMYSQL | MySQL |
QOCI | Oracle Call Interface Driver |
QODBC | Open Database Connectivity(ODBC)包括Microsoft SQL Server和其他ODBC 兼容数据库 |
QPSQL | PostgreSQL版本6.x和7.x |
QSQLITE | SQLite版本3及以上版本 |
QSQLITE2 | SQLite版本2 |
QTDS | Sybase Adaptive Server |
访问数据库的高层类
类 名 | 用 途 |
QSqlQueryModel | 基于任意SQL语句的只读模型 |
QSqlTableModel | 基于单个表的读写模型 |
QSqlRelationalTableModel | QSqlTableModel的子类,增加了外键支持 |
这三个类均从QAbstractTableModel类继承,在不涉及数据的图形表示时可以单独使用以进行数据库操作,也可以作为数据源将数据库内的数据在QListView或QTableView等基于视图模式的Qt类中表示出来。使用它们的另一个好处是,程序员很容易在编程时采用不同的数据源。例如,假设起初打算使用数据库存储数据并使用了QSqlTableModel类,后因需求变化决定改用XML文件存储数据,程序员此时要做的仅是更换数据模型类。
QSqlRelationalTableModel类是对QSqlTableModel类的扩展,它提供了对外键的支持。外键是一张表中的某个字段与另一张表中的主键间的一一映射。
在此,一旦数据库连接建立后,就可以使用QSqlQuery执行底层数据库支持的SQL语句,此方法所要做的仅是创建一个QSqlQuery对象,然后再调用QSqlQuery::exec()函数。
占位符通常使用包含non-ASCII字符或非non-Latin-1字符的二进制数据和字符串。无论数据库是否支持Unicode编码,Qt在后台均使用Unicode字符。对于不支持Unicode编码的数据库,Qt将进行隐式的字符串编码转换。
(3) 运行结果如下图所示。
———————————————
觉得有用的话请关注点赞,谢谢您的支持!
对于本系列文章相关示例完整代码有需要的朋友,可关注并在评论区留言!