QT SQL使用指南
QtSql模块提供了与平台无关的访问SQL数据库的接口,这个接口由利用Qt的模型/视图机构将数据库与用户界面集成的一套类来支持。
- QSqlDatabase对象表征了数据库的关联。
- QSqlQuery类提供了一种直接执行任意的SQL语句并处理其结果的方式。
- QSqlTableModel和QSqlRelationalTableModel可以用来遍历和编辑数据,也可以添加用户可以查看和修改的视图。
1. 链接数据库
QSqlDatabase *QSqlDatabase::addDatabase
( const QString & type,const QString & connectionName= defaultConnection ) [static]
Adds a database to the list of
database connections usingthe drivertype and the connection nameconnectionName.
调用QSqlDatabase的静态函数addDatabase()创建一个QSqlDatabase对象,指定数据库驱动类型和链接名称,链接名称不指定则设为默认链接,直到程序关闭或者调removeDatabase()函数,才关闭该链接。调用setDatabaseName()函数创建数据库文件。
QSqlDatabase db = QSqlDatabase::addDatabase(databasename);
db.setDatabaseName(databasefilename);
打开数据库:
bool db.open();
查看QT支持数据库类型:
QStringListdrivers=QSqlDatabase::drivers();
qDebug()<<drivers;
验证是否支持SQLite:
if(!QSqlDatabase::drivers().contains("QSQLITE"))
{
QMessageBox::critical(0, QString("数据库警告"), QString("没有驱动SQLITE"),QMessageBox::Cancel, QMessageBox::NoButton);
}
查看数据库文件里有哪些表,是否含有一张表:
QStringListtables=db.tables();
qDebug()<<tables;
if(!tables.contains("numsetdatabase"))
2. 查询数据库
QSqlQuery类提供了一种执行和操纵SQL语句的方式。
QSqlQuery包括了所有的功能,像在一个QSqlDatabase上执行SQL查询创建、导航和索取数据等等。它可以执行DML(数据操作语言)语句,比如SELECT, INSERT, UPDATE 和 DELETE, 还可以执行DDL(数据定义语言)语句,比如:CREATE TABLE。不仅如此,它还可以用于执行特定数据库而不是标准SQL语句的指令(比如 SET DATESTYLE=ISO 用于 PostgreSQL).
成功执行一SQL语句设置query为活动状态( isActive() 返回TRUE)否则查询置为非活动状态。无论那种情况,执行一条新的 SQL 语句之后,查询都被定位在一个无效的记录上;所以在数据被获取之前,查询都必须被导航到一个有效的记录上(isValid()可以确定查询是否处在一个有效的记录上)。
在记录之间进行导航用以下一条函数:
next()
prev()
first()
last()
seek(int)
这些函数允许程序员向前、向后或随心所欲地在查询返回的记录之间移动。一旦一个处于活动状态的查询被定位在一个有效的记录上时,就可以通过value()获取数据。所有的数据都被用QVariants从SQL后端转换过来。比如:
QSqlQuery query( "select name from customer" );
while ( query.next() ) {
QString name = query.value(0).toString();
doSomething( name );
}
要获得从一个查询中返回的数据,用value()方法。由SELECT语句返回的数据中的每个域可以通过指定一个索引来访问,索引从0开始。没有一个方法可以通过指定名字来定位域的,因为这不能得到可能确认。(请看QSqlCursor,它提供一个更加有弹性的接口用于从一个表单中选择数据或在一个数据库中查看数据)。
1. 把SQL查询指定为QSqlQuery::exec()的参数,也可以把它传递给构造函数,构造函数或立即执行。
QSqlQuery query; query.exec("createtablenumsetdatabase(idintprimarykey,numint)");
QSqlQuery query("createtablenumsetdatabase(idintprimarykey,numint)");
2. 如果需要插入多条记录或者不想将数值转换为字符串,可以使用prepare()
bool QSqlQuery::prepare (const QString & query )
Oracle风格:
QSqlQuery query;
query.prepare("INSERT INTO person (id,forename, surname) "
"VALUES (:id, :forename,:surname)");
query.bindValue(":id",1001);
query.bindValue(":forename","Bart");
query.bindValue(":surname","Simpson");
query.exec();
ODBC风格:
QSqlQuery query;
query.prepare("INSERT INTO person(id, forename, surname) "
"VALUES (?, ?, ?)");
query.addbindValue(1001);
query.addbindValue("Bart");
query.addbindValue("Simpson");
query.exec();
3. 创建多数据库链接
QSqlDatabase db =QSqlDatabase::addDatabase(“database1”, “database2”);
db.setDatabaseName(“databasefile”);
使用其中一个数据库:
QSqlDatabase db = QSqlDatabase::database(“database1”);
将该数据库对象指针db传递到QSqlQuery的构造函数,使用该链接:
QSqlQuery query(db);
4. 优化查询
(1)成功执行一SQL语句设置query为活动状态( isActive() 返回TRUE)否则查询置为非活动状态。无论那种情况,执行一条新的 SQL 语句之后,查询都被定位在一个无效的记录上;所以在数据被获取之前,查询都必须被导航到一个有效的记录上(isValid()可以确定查询是否处在一个有效的记录上)。
QSqlQuery query("createtablenumsetdatabase(idintprimarykey,numint)");
if(!query.isActive())
{
发生错误,否者呈激活状态;
}
(2)在操作一个大数据库时,为了便于优化,可以在调用exec()前调用QSqlQuery::setForwardOnly(true),然后使用next()遍历查询结果。
QSqlDatabase::database().transaction();
QSqlQueryhost_number_query;
host_number_query.setForwardOnly(true);
host_number_query.exec("selectnumfromnumsetdatabase");
while(host_number_query.next())
{
host=host_number_query.value(0).toInt();
qDebug()<<host;
}
QSqlDatabase::database().commit();
3. SQL事务处理
SQLite数据库本质上来讲就是一个磁盘上的文件,所以一切的数据库操作其实都会转化为对文件的操作,而频繁的文件操作将会是一个很好时的过程,会极大地影响数据库存取的速度。例如:向数据库中插入100万条数据,在默认的情况下如果仅仅是执行query.exec("insert into DataBase(......) values(......)");就会打开和关闭文件100万次,所以速度当然会很慢。SQLite数据库是支持事务操作的,于是我们就可以通过事务来提高数据库的读写速度。事务的基本原理是:数据库管理系统首先会把要执行的sql语句存储到内存当中,只有当commit()的时候才一次性全部执行所有内存中的数据库。
默认情况下,SQLite的每一条exec()语句就是一个事务。在数据库访问、修改的并发性和频率较高时,为了达到数据同步的需要,防止在修改数据库数据的同时有其他操作发生导致数据不一致,最好采用事务处理。
bool QSqlDatabase::transaction ()
Beginsa transaction on the database if the driver supports transactions. Returns true ifthe operation succeeded. Otherwise it returns false.
bool QSqlDatabase::commit ()
Commitsa transaction to the database if the driver supports transactions and a transaction() has been started. Returnstrue ifthe operation succeeded. Otherwise it returns false.
Note: Forsome databases, the commit will fail and return false ifthere is an active query usingthe database for aSELECT.Make the query inactive beforedoing the commit.
bool QSqlDatabase::rollback ()
Rollsback a transaction on the database, if the driver supports transactions and a transaction() has been started. Returnstrue ifthe operation succeeded. Otherwise it returns false.
Note: Forsome databases, the rollback will fail and return false ifthere is an active query usingthe database for aSELECT.Make the query inactive beforedoing the rollback.
#include <QtCore/QCoreApplication>
#include <QtSql>
#include <iostream>
using namespace std;
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QSqlDatabase db_sqlite = QSqlDatabase::addDatabase("QSQLITE", "connSQLite");
db_sqlite.setDatabaseName("SQLiteDB.db3");
db_sqlite.open();
QSqlQueryquery("", db_sqlite);
bool bsuccess = false;
QTime tmpTime;
// 开始启动事务
db_sqlite.transaction();
tmpTime.start();
for(int i = 0; i<100000; i++)
{
bsuccess = query.exec("insert into DataBase(D_1,D_2,D_3,D_4,D_5) values('TT','TT','TT','TT','TT')");
if (!bsuccess)
{
cout<<"Error occur"<<endl;
break;
}
}
// 提交事务,这个时候才是真正打开文件执行SQL语句的时候
db_sqlite.commit();
cout<<"10000条数据耗时:"<<tmpTime.elapsed()<<"ms"<<endl;
}
其实Qt操作sqlite数据库增加事务的功能就是上面例子中有注释的两句话,如果去掉这两句话,程序又会还原为:打开文件——执行query.exec(...)——关闭文件。大家可以试一下,添加这两句即增加事务以后数据插入的速度明显提高很多。