Qt4_连接与查询

连接与查询

为了执行 SQL 查询,首先必须建立与数据库的连接。通常情况下,是在应用程序开始时所调用的一个单独的函数中建立数据库连接。例如:

bool createConnection()
{
    QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL");
    db.setHostName("mozart.konkordia.edu");
    db.setDatabaseName("musicdb");
    db.setPassword("T17aV44");
    if(!db.open())
    {
        QMessageBox::critical(0, QObject::tr("Database Error"), db.lastError().text());
        return false;
    }
    return false;
}

首先,调用QSqlDatabase::addDatabase()来创建QSqlDatabase 对象。addDatabase()的第一个参数指定了Qt必须使用哪一个数据库驱动程序来访问这个数据库,这里使用的是MySQL。

接下来,设置数据库的主机名、数据库名、用户名和密码,并且打开这个连接。如果open()操作失败,将显示出错信息。

通常情况下,是在main()中调用createConnection()的:

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    if(!createConnection())
       return 1;
    ...
    return app.exec();
}

一旦连接建立,就可以使用QSqlQuery执行底层数据库支持的任何SQL语句了。例如,一下是如何执行SELECT语句的代码:

QSqlQuery query;
query.exec("SELECT tile, year FROM cd WHERE year >= 1998");

在exec()调用之后,可以遍历查询的结果集:

while(query.next())
{
    QString title = query.value(0).toString();
    int year = query.value(1).toInt();
    std::cerr << qPrintable(title) << ": " << year << std::endl;
}

只要调用next()一次,就可以把这个QSqlQuery定位到结果集中的第一条记录。随后的next()调用每次都会把记录指针前移一条记录,直到到达结尾时next()才返回false。如果结果集为空(或查询失败),那么next()的第一次调用将返回false。

value()函数把字段值作为QVariant返回。字段是按照SELECT语句中给定的顺序从编号0开始的。这个QVariant类可以保存许多C++和Qt数据类型,包括int和QSting可存储于数据库中的不同数据类型都可以映射为相应的C++和Qt类型并且存储到QVariant中。例如VARCHAR代表着 QString,而 DATETIME代表着 QDateTime。

QSqIQuery提供了一些可以遍历结果集的函数:first()、ast()、previous()和seek()。这些函数都很方便,不过对于某些数据库,它们可能会比next()更慢或者更加耗费内存。在操作一个大数据集时为了便于优化可以在调用exec()之前调用OSqlQuery::setForwardOnly(true),然后只使用next()遍历结果集。

之前,我们把这个SQL查询指定为QSqlQuery::exec()的参数,然而还可以直接把它传递给构造函数构造函数会立即执行SOL查询:

QSqlQuery query("SELECT title, year FROM cd WHERE year >= 1998");

可以通过对查询调用 isActive()来检查是否有错误发生:

if(!query.isActive())
   QMessageBox::warning(this, tr("Database Error"), query.lastError().text());

如果没有错误发生,则查询会变成"激活"状态,然后就可以使用next()来遍历结果集。

执行INSERT的代码与执行SELECT的一样容易:

QSqlQuery query("INSERT INTO cd(id, artistid, title, year)" "VALUES(203, 102, 'Living in America', 2002)");

在这之后,numRowsAffected()返回受 SQL 语句影响的行数(如果发生错误,就返回 -1)。

如果需要插入多条记录,或者想避免将数值转换成为字符串(并且正确地转义它们) ,可以使prepare()来指定一个包含占位符的查询,然后赋值绑定想插入的数值。Qt对所有数据库都支持Oracle风格和 ODBC 风格的占位符语法,如果它们可用,就使用本地支持;如果不可用,就模拟它的功能。以下是使用 Oracle 风格语法及命名占位符的实例:

QSqlQuery query;
query.prepare("INSERT INTO cd(id, artistid, title, year)" "VALUES(:id, :artistid, :title, :year)");
query.bindValue(":id", 203);
query.bindValue(":artistid", 102);
query.bindValue(":title", "Living in America");
query.bindValue(":year", 2002);
query.exec();

以下是一个使用ODBC风格位置占位符的相同实例:

QSqlQuery query;
query.prepare("INSERT INTO cd(id, artistid, title, year)" "VALUES(?, ?, ?, ?)");
query.bindValue(203);
query.bindValue(102);
query.bindValue("Living in America");
query.bindValue(2002);
query.exec();

在exec()调用之后可以调用bindValue()或者addBindValue()来赋值绑定新值,然后再次调用exec()并利用这些新值执行查询。

占位符通常用于指定二进制数据或者包含非ASCII码或者非Latin字符的字符串。在底层,Qt对支持Unicode的数据库使用Unicode编码而对于不支持Unicode的,Qt会明确地把字符串转换为合适的编码方式。

如果数据库中SQL事务处理可用的话,Qt就支持它。为了开始事务处理,需对代表数据库连接的QSqlDatabase对象调用transaction()。为了结束事务处理,可以调用commit()或者rollback()。例如,以下是如何查询外键并且在事务处理中执行INSERT语句的代码:

QSqlDatabase::database().transaction();
QSqlQuery query;
query.exec("SELECT id FROM artist WHERE name = 'Gluecifer'");
if(query.next())
{
    int artistId = query.value(0).toInt();
    query.exec("INSERT INTO cd(id, artistid, title, year)" "VALUES(201, "+QString::number(artistId) + ", 'Riding the Tiger', 1997)");
}
QSqlDatabase::database().commit();

QSqlDatabase::database()函数返回一个表示在createConnetion()中创建的连接的QSqlDatabase对象。如果事务处理不能启动,QSqlDatabase::transaction()就返回false值。一些数据库不支持事物处理。对于这类数据库,transaction()、commit()和rsollback()几个函数就什么也不做。可以使用hasFeature()对数据库相关的QSqlDriver进行测试,看看这个数据库是不是支持事务处理。

QSqlDriver *driver = QSqlDatabase::database().driver();
if(driver->hasFeature(QSqlDriver::Transactions))
   ...

还可以测试其他的一些数据库特征,包括数据库是否支持BLOB(二进制大对象)、Unicode以及经过处理的查询。

使用QSqlDriver::handle()和QSqlResult::handle()函数,还可以读取低级数据库驱动程序句柄和查询结果集的低级句柄。但如果不清楚其使用目的与细节的话,这两函数的使用非常容易出错。

目前为止,在我们研究的实例中,都是假设应用程序所使用的是单一的数据库连接。如果想创建多个连接,可以把数据库名作为第二个参数传递给addDatabase()。例如:

QSqlDatabase db = QSqlDatabase::addDatabase("QPSQL", "OTHER");
db.setHostName("saturn.mcmanamy.edu");
db.setDatabaseName("startsdb");
db.setUserName("hilbert");
db.setPassword("ixtapa7");

然后,可以通过把数据库名传递给QSqlDatabase::database()得到指向QSqlDatabase对象的指针:

QSqlDatabase db = QSqlDatabase::database("OTHER");

为了使用其他连接执行查询,我们把 SqlDatabase对象传递给 QSqlQuery 的构造函数:

QSqlQuery query(db);
query.exec("SELECT id FROM artist WHERE name = 'Mando Dia'");

如果想一次执行多个事务处理,多重连接是很有用的,因为每个连接只能处理一个有效的事务处理。当使用多个数据库连接时,还可以有一个命名的连接,而且如果没有具体指定的话,QSqlQuery 就会使用这个未命名的连接。

除了 QSqlQuery可之外,Qt 提供了 QSqlTableModel 类作为一个高级界面接口,让我们不必使用原始的 SQL 语句来执行大多数常用的 SQL 操作(如SELECT、INSERT、UPDATE和DELETE)。这个类可以用来独立处理数据库而不涉及任何的图形用户界面,它也可以用作 QListView或QTableView的数据源。

以下是使用QSqlTableModel执行SELECT操作的实例:

QSqlTableModel model;
model.setTable("cd");
model.setTable("year >= 1998");
model.select();

这等价于如下的查询:

SELECT *FROM cd WHERE year >= 1998

利用 QSqlTableModel::record()获得某一给定的记录,或者利用value()读取单独的字段,我们可以遍历这个结果集:

for (int i = 0; i<model.rowCount(); ++i)
{
    QSqlRecord record = model.record(i);
    QString title = record.value("title").toString();
    int year = record.value("year").toInt();
    std::cerr << qPrintable(title) << ": " << year << std::endl;
}

QSqlRecord::value()函数既可以接收字段名也可以接收字段索引。在对大数据集进行操作建议利用索引来指定字段。例如:

int tileIndex = model.record().indexOf("title");
int yearIndex = model.record().indexOf("year");
for(int i=0; i<model.rowCount(); ++i)
{
    QSqlRecord record = model.record(i);
    QString title = record.value(titleIndex).toString();
    int year = record.value(yearIndex).toInt();
    std::cerr << qPrintable(title) << ": " << year << std::endl;
}

为了在数据库表中插入记录,可调用 insertRow()来创建一个新的空行(记录),然后使用setData设置每一个列(字段)的值:

QSqlTableModel model;
model.setTable("cd");
int row = 0;
model.insertRows(row, 1);
model.setData(model.index(row, 0), 113);
model.setData(model.index(row. 1), "Shanghai My Heart");
model.setData(model.index(row, 3), 2003);
model.submitAll();

在调用submitAll() 之后,记录可能会被移动到一个不同的行位置,这取决于表是如何排序的。如果插入失败,submittAll()调用将返回 false。

SQL 模型与标准模型之间最大的区别在于:对于 SQL 模型,必须调用地mitAll()以将发生的更改写入数据库。

为了更新某一记录,首先必须把 QSqlTableModel 定位到要修改的记录上[例如使用 select() ]。然后提取这条记录,更新想改变的字段并将更改过的数据目写到数据库中:

QSqlTableModel model;
model.setTable("cd");
model.setFilter("id=125");
model.select();
if(model.rowCount() == 1)
{
    QSqlRecord record = model.record(0);
    record.setValue("year", record.value("year").toInt() + 1);
    model.setRecord(0, record);
    model.submitAll();
}

如果有一条记录与指定的过滤器相匹配,就利用 QSqlTableModel::record()获得这条记录。并用修改后的记录复写原始的记录。

正如对非 SQL 模型所作的处理一样,也可以使用setData() 来执行更新。我们获得的模型索引都是针对给定的行与列的:

model.select();
if(model.rowCount() == 1)
{
    model.setData(model.index(0, 1), "Melody A.M.");
    model.setData(model.index(0, 3), model.data(model.index(0, 3)).toInt() + 1);
    model.submitAll();
}

删除记录与更新记录的过程很相似:

model.setTable("cd");
model.setFilter("id = 125");
if(model.rowCount() == 1)
{
    model.removeRows(0, 1);
    model.submitAll();
}

removeRows()调用删除了第一条记录的行号以及记录号。下面的实例则删除了所有与过滤器匹配的记录:

model.setTable("cd");
model.setFilter("year<1990");
model.select();
if(model.rowCount()>0)
{
    model.removeRows(0, mode.rowCount());
    model.submitAll();
}

QSqlQuerγ和QSqlTableModel 这两个类提供了。和 SQL 数据库之间的接口。利用这些类,可以创建显示用户数据以及让用户插入、更新和删除记录的表单。

对于使用 SQL 类的应用程序,需要将如下的命令行添加到其 .pro 文件中:

QT += sql

这将确保应用程序可以连接到QtSql库。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阳光开朗男孩

你的鼓励是我最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值