2.Books

Books示例说明了Qt中SQL类如何被Model/View框架使用,使用数据库中存储的信息,创建丰富的用户界面。

 

首先介绍使用SQL我们需要了解的类:

1.QSqlDatabase:

QSqlDatabase类表示与数据库的连接。

QSqlDatabase类提供了一个通过连接访问数据库的接口。 QSqlDatabase的一个实例表示连接。连接通过驱动(驱动就是QSqlDriver派生类)访问数据库。或者,你自己子类化QSqlDriver,编写自己的数据库驱动。

通过调用静态addDatabase()函数创建一个连接。addDatabase()函数需要2个参数,第一个指定的驱动,第二个连接名称。Qt支持的驱动如下:

驱动数据库
QDB2 IBM DB2 (7.1 或更新版本)
QIBASE Borland InterBase
QMYSQL MySQL
QOCI Oracle Call Interface Driver
QODBC Open Database Connectivity (ODBC) – Microsoft SQL Server 及其它兼容 ODBC 的数据库
QPSQL PostgreSQL (7.3 或更新版本)
QSQLITE2 SQLite 2
QSQLITE SQLite 3
QSYMSQL 针对 Symbian 平台的SQLite 3
QTDS Sybase Adaptive Server (自 Qt 4.7 起废除)

连接通过设定的连接名称区分,可以多个连接连接到同一个数据库。 QSqlDatabase还支持默认连接的概念,连接未命名就是默认连接。 要创建默认连接,调用addDatabase()时不要传递连接名参数。随后,当您调用任何使用连接名称参数的静态成员函数时,如果不传递连接名称参数,则假定为默认连接。以下代码段显示了如何创建和打开与PostgreSQL数据库的默认连接:

QSqlDatabase db = QSqlDatabase::addDatabase("QPSQL");
db.setHostName("acidalia");
db.setDatabaseName("customdb");
db.setUserName("mojito");
db.setPassword("J0a1m8");
bool ok = db.open();

创建QSqlDatabase对象后,使用setDatabaseName(),setUserName(),setPassword(),setHostName(),setPort()和setConnectOptions())设置连接参数。连接初始状态是不可用的,直到你调用open()函数激活与数据库的物理连接。

上面定义的连接将是默认连接,因为我们没有给addDatabase()提供连接名称。 随后,您可以通过调用没有连接名称参数的database()来获取默认连接:

QSqlDatabase db = QSqlDatabase::database();

QSqlDatabase是一个value class。通过QSqlDatabase实例改变一个连接,会影响其他代表同一个连接的实例。所以使用cloneDatabase()函数创建一个独立的连接,修改这个独立连接而不影响其他连接。

如果你创建多个数据库连接,请在调用addDatabase()时为每个连接指定唯一的连接名称。使用具有连接名称的database()来获取该连接。使用带有连接名称的removeDatabase()来删除连接。如果您尝试删除由其他QSqlDatabase对象引用的连接,QSqlDatabase将输出警告。 使用contains()来查看给定的连接名称是否在连接列表中。

一旦建立连接,您可以调用tables()获取数据库中的表的列表,调用primaryIndex()获取表的主索引,并调用record()获取有关表的字段的元信息。

注意:不推荐使用QSqlDatabase :: exec()。 推荐用QSqlQuery :: exec()。

如果驱动程序支持事务,请使用transaction()来启动一个事务,commit()或rollback()来完成它。使用hasFeature()来询问驱动程序是否支持事务。注意:使用事务时,必须先创建事务,然后再创建查询。

如果发生错误,lastError()将返回有关它的信息。

使用drivers()获取可用的SQL驱动程序的名称。 使用isDriverAvailable()检查特定驱动程序的存在。 如果您创建了自己的自定义驱动程序,则必须使用registerSqlDriver()注册它。

 

2.QSqlQuery

QSqlQuery类提供了一种执行和操作SQL语句的方法。

QSqlQuery封装了在QSqlDatabase中执行的SQL操作。它可以用于执行DML(数据操作语言)语句,如SELECT,INSERT,UPDATE和DELETE以及DDL(数据定义语言)语句,如CREATE TABLE。 它也可以用于执行不是标准SQL的数据库特定的命令(例如PostgreSQL数据库 SET DATESTYLE = ISO)。

成功执行SQL语句将query的状态设置为active,以使isActive()返回true。否则query的状态设置为inactive。在任一情况下,执行新的SQL语句时,query位于无效记录上。 必须将active query导航到有效的记录(isValid()返回true)才能检索到值。

对于某些数据库,当active query是SELECT语句时,当你调用commit()和rollback()函数处理事务时,会失败。当active query是SELECT语句时,必须调用finish()和clear()函数,使得query变成incative,再调用事务。

使用以下函数执行导航记录:

next()

previous()

first()

last()

seek()

这些函数允许程序员向前,向后或任意地移动query返回的记录。如果您仅需要前进的结果(例如,使用next()),则可以使用setForwardOnly(),这将节省大量内存开销并提高某些数据库的性能。一旦active query位于有效的记录上,可以使用value()检索数据。 所有从SQL端传送过来的数据都是QVariant类型。

举例:

QSqlQuery query("SELECT country FROM artist");
while (query.next()) {
    QString country = query.value(0).toString();
    doSomething(country);
}

要获得查询返回的数据,请使用value(int)。字段从0开始,比如"SELECT country FROM artist"只有一个字段。我们使用query.value(0)就可以获得QVariant类型的值。"SELECT forename, surname FROM people;" 此时forename字段0,surname 字段1.我们要获得surname的值query.value(1).toString()。所以使用select *是不建议的,因为你无法确定字段的顺序。

如果我们硬是要使用select *怎么办?先调用record()函数获得整个查询的记录,然后调用indexOf()函数,从记录中查找字段的位置。

QSqlQuery query("SELECT * FROM artist");
int fieldNo = query.record().indexOf("country");
while (query.next()) {
         QString country = query.value(fieldNo).toString();
         doSomething(country);
}

QSqlQuery支持预查询执行和占位符参数绑定。某些数据库不支持这些功能,因此对于这些数据库,Qt会模拟所需的功能。例如,Oracle和ODBC驱动程序具有预查询支持,Qt利用它; 但是对于不支持这种操作的数据库,Qt自身实现了这个特征。例如:在查询操作时,用占位符替换实际的值,使用numRowsAffected()函数获得非SELECT查询影响的行数。使用size()函数获得SELECT语句查询的行数。

Oracle数据库通过使用冒号语法来标识占位符,例如 :name。 ODBC只是使用?字符来标识占位符。Qt两种语法都支持,但是限制是不能混合使用。

您可以通过boundValues()函数由一个变量(QMap<QString, QVariant>)获得所有字段的值。

下面我们将介绍4中不同的占位符参数绑定方法和1中绑定值到存储过程的示例:

1.通过字段的名称绑定。

     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();    

2.通过字段的位置绑定

     QSqlQuery query;
     query.prepare("INSERT INTO person (id, forename, surname) "
                   "VALUES (:id, :forename, :surname)");
     query.bindValue(0, 1001);
     query.bindValue(1, "Bart");
     query.bindValue(2, "Simpson");
     query.exec(); 

3.通过字段位置绑定,占位符由 :something 换成 ?

     QSqlQuery query;
     query.prepare("INSERT INTO person (id, forename, surname) "
                   "VALUES (?, ?, ?)");
     query.bindValue(0, 1001);
     query.bindValue(1, "Bart");
     query.bindValue(2, "Simpson");
     query.exec();

4.通过字段位置绑定, addBindValue()函数和查query语句中占位符的位置一致。

     QSqlQuery query;
     query.prepare("INSERT INTO person (id, forename, surname) "
                   "VALUES (?, ?, ?)");
     query.addBindValue(1001);
     query.addBindValue("Bart");
     query.addBindValue("Simpson");
     query.exec();

 

绑定值到存储过程。

下面的代码调用一个叫做AsciiToInt()的存储过程。第一个参数字符,第二个参数存储过程的返回值。

     QSqlQuery query;
     query.prepare("CALL AsciiToInt(?, ?)");
     query.bindValue(0, "A");
     query.bindValue(1, 0, QSql::Out);
     query.exec();
     int i = query.boundValue(1).toInt(); // i is 65

QSql::Out 表示绑定值从数据库获得数据。

请注意,未绑定的参数将返回其本身的值。

存储过程使用return语句返回值或者返回多个结果集,Qt并不是完全支持。

注意:在创建QSqlQuery之前,必须加载SQL驱动程序并打开连接。 另外,执行query操作时,连接必须保持打开; 否则,QSqlQuery的行为是未定义的。

 

3.QSqlError

QSqlError类提供SQL数据库错误信息。

QSqlError对象可以提供数据库明确的错误信息。driverText()返回数据库驱动得到的错误信息,databaseText()返回数据库得到的错误消息(两者合并就是text()),number()返回错误数量,type()返回错误类型。 这些函数都具有设置器,以便您可以从自己的类创建和返回QSqlError对象,例如从你自定义SQL驱动程序。

 


 

posted @ 2017-06-13 01:31  billxyd  阅读(248)  评论(0编辑  收藏  举报