SQL即结构化查询语言,是关系数据库的标准语言。前面已经提到,在Qt中利用QSqlQuery类实现了执行SQL语句。需要说明,我们这里只是Qt教程,而非专业的数据库教程,所以我们不会对数据库中的一些知识进行深入讲解,下面只是对最常用的几个知识点进行讲解。
我们下面先建立一个工程,然后讲解四个知识点,分别是:
一,操作SQL语句返回的结果集。
二,在SQL语句中使用变量。
三,批处理操作。
四,事务操作。
我们新建Qt4 Gui Application工程,我这里工程名为query ,然后选中QtSql模块,Base class选QWidget。工程建好后,添加C++ Header File ,命名为connection.h ,更改其内容如下:
#ifndef CONNECTION_H
#define CONNECTION_H
#include <QMessageBox>
#include <QSqlDatabase>
#include <QSqlQuery>
static bool createConnection()
{
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
db.setDatabaseName(":memory:");
if (!db.open()) {
QMessageBox::critical(0, qApp->tr("Cannot open database"),
qApp->tr("Unable to establish a database connection."
), QMessageBox::Cancel);
return false;
}
QSqlQuery query;
query.exec("create table student (id int primary key, "
"name varchar(20))");
query.exec("insert into student values(0, 'first')");
query.exec("insert into student values(1, 'second')");
query.exec("insert into student values(2, 'third')");
query.exec("insert into student values(3, 'fourth')");
query.exec("insert into student values(4, 'fifth')");
return true;
}
#endif // CONNECTION_H
然后更改main.cpp的内容如下:
#include <QtGui/QApplication>
#include "widget.h"
#include "connection.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
if (!createConnection())
return 1;
Widget w;
w.show();
return a.exec();
}
可以看到,我们是在主函数中打开数据库的,而数据库连接用一个函数完成,并单独放在一个文件中,这样的做法使得主函数很简洁。我们今后使用数据库时均使用这种方法。我们打开数据库连接后,新建了一个学生表,并在其中插入了几条记录。
表中的一行就叫做一条记录,一列是一个属性。这个表共有5条记录,id和name两个属性。程序中的“id int primary key”表明id属性是主键,也就是说以后添加记录时,必须有id项。
下面我们打开widget.ui文件,在设计器中向界面上添加一个Push Button ,和一个Spin Box 。将按钮的文本改为“查询”,然后进入其单击事件槽函数,更改如下。
void Widget::on_pushButton_clicked()
{
QSqlQuery query;
query.exec("select * from student");
while(query.next())
{
qDebug() << query.value(0).toInt() << query.value(1).toString();
}
}
我们在widget.cpp中添加头文件:
#include <QSqlQuery>
#include <QtDebug>
然后运行程序,单击“查询”按钮,效果如下:
可以看到在输出窗口,表中的所有内容都输出出来了。这表明我们的数据库连接已经成功建立了。
一,操作SQL语句返回的结果集。
在上面的程序中,我们使用query.exec("select * from student");来查询出表中所有的内容。其中的SQL语句“select * from student”中“*”号表明查询表中记录的所有属性。而当query.exec("select * from student");这条语句执行完后,我们便获得了相应的执行结果,因为获得的结果可能不止一条记录,所以我们称之为结果集。
结果集其实就是查询到的所有记录的集合,而在QSqlQuery类中提供了多个函数来操作这个集合,需要注意这个集合中的记录是从0开始编号的。最常用的有:
seek(int n) :query指向结果集的第n条记录。
first() :query指向结果集的第一条记录。
last() :query指向结果集的最后一条记录。
next() :query指向下一条记录,每执行一次该函数,便指向相邻的下一条记录。
previous() :query指向上一条记录,每执行一次该函数,便指向相邻的上一条记录。
record() :获得现在指向的记录。
value(int n) :获得属性的值。其中n表示你查询的第n个属性,比方上面我们使用“select * from student”就相当于“select id, name from student”,那么value(0)返回id属性的值,value(1)返回name属性的值。该函数返回QVariant类型的数据,关于该类型与其他类型的对应关系,可以在帮助中查看QVariant。
at() :获得现在query指向的记录在结果集中的编号。
需要说明,当刚执行完query.exec("select * from student");这行代码时,query是指向结果集以外的,我们可以利用query.next(),当第一次执行这句代码时,query便指向了结果集的第一条记录。当然我们也可以利用seek(0)函数或者first()函数使query指向结果集的第一条记录。但是为了节省内存开销,推荐的方法是,在query.exec("select * from student");这行代码前加上query.setForwardOnly(true);这条代码,此后只能使用next()和seek()函数。
下面将“查询”按钮的槽函数更改如下:
void Widget::on_pushButton_clicked()
{
QSqlQuery query;
query.exec("select * from student");
qDebug() << "exec next() :";
if(query.next())
//开始就先执行一次next()函数,那么query指向结果集的第一条记录
{
int rowNum = query.at();
//获取query所指向的记录在结果集中的编号
int columnNum = query.record().count();
//获取每条记录中属性(即列)的个数
int fieldNo = query.record().indexOf("name");
//获取"name"属性所在列的编号,列从左向右编号,最左边的编号为0
int id = query.value(0).toInt();
//获取id属性的值,并转换为int型
QString name = query.value(fieldNo).toString();
//获取name属性的值
qDebug() << "rowNum is : " << rowNum //将结果输出
<< " id is : " << id
<< " name is : " << name
<< " columnNum is : " << columnNum;
}
qDebug() << "exec seek(2) :";
if(query.seek(2))
//定位到结果集中编号为2的记录,即第三条记录,因为第一条记录的编号为0
{
qDebug() << "rowNum is : " << query.at()
<< " id is : " << query.value(0).toInt()
<< " name is : " << query.value(1).toString();
}
qDebug() << "exec last() :";
if(query.last())
//定位到结果集中最后一条记录
{
qDebug() << "rowNum is : " << query.at()
<< " id is : " << query.value(0).toInt()
<< " name is : " << query.value(1).toString();
}
}
然后在widget.cpp文件中添加头文件。
#include <QSqlRecord>
运行程序,结果如下:
二,在SQL语句中使用变量。
我们先看下面的一个例子,将“查询”按钮的槽函数更改如下:
void Widget::on_pushButton_clicked()
{
QSqlQuery query;
query.prepare("insert into student (id, name) "
"values (:id, :name)");
query.bindValue(0, 5);
query.bindValue(1, "sixth");
query.exec();
//下面输出最后一条记录
query.exec("select * from student");
query.last();
int id = query.value(0).toInt();
QString name = query.value(1).toString();
qDebug() << id << name;
}
运行效果如下:
可以看到,在student表的最后又添加了一条记录。在上面的程序中,我们先使用了prepare()函数,在其中利用了“:id”和“:name”来代替具体的数据,而后又利用bindValue()函数给id和name两个属性赋值,这称为绑定操作。其中编号0和1分别代表“:id”和“:name”,就是说按照prepare()函数中出现的属性从左到右编号,最左边是0 。这里的“:id”和“:name”,叫做占位符,这是ODBC数据库的表示方法,还有一种Oracle的表示方法就是全部用“?”号。如下:
query.prepare("insert into student (id, name) "
"values (?, ?)");
query.bindValue(0, 5);
query.bindValue(1, "sixth");
query.exec();
我们也可以利用addBindValue()函数,这样就可以省去编号,它是按顺序给属性赋值的,如下:
query.prepare("insert into student (id, name) "
"values (?, ?)");
query.addBindValue(5);
query.addBindValue("sixth");
query.exec();
当用ODBC的表示方法时,我们也可以将编号用实际的占位符代替,如下:
query.prepare("insert into student (id, name) "
"values (:id, :name)");
query.bindValue(":id", 5);
query.bindValue(":name", "sixth");
query.exec();
以上各种形式的表示方式效果是一样的。特别注意,在最后一定要执行exec()函数,所做的操作才能被真正执行。
下面我们就可以利用绑定操作在SQL语句中使用变量了。
void Widget::on_pushButton_clicked()
{
QSqlQuery query;
query.prepare("select name from student where id = ?");
int id = ui->spinBox->value(); //从界面获取id的值
query.addBindValue(id); //将id值进行绑定
query.exec();
query.next(); //指向第一条记录
qDebug() << query.value(0).toString();
}
运行程序,效果如下:
我们改变spinBox的数值大小,然后按下“查询”按钮,可以看到对应的结果就出来了。
三,批处理操作。
当要进行多条记录的操作时,我们就可以利用绑定进行批处理。看下面的例子。
void Widget::on_pushButton_clicked()
{
QSqlQuery q;
q.prepare("insert into student values (?, ?)");
QVariantList ints;
ints << 10 << 11 << 12 << 13;
q.addBindValue(ints);
QVariantList names;
names << "xiaoming" << "xiaoliang" << "xiaogang" << QVariant(QVariant::String);
//最后一个是空字符串,应与前面的格式相同
q.addBindValue(names);
if (!q.execBatch()) //进行批处理,如果出错就输出错误
qDebug() << q.lastError();
//下面输出整张表
QSqlQuery query;
query.exec("select * from student");
while(query.next())
{
int id = query.value(0).toInt();
QString name = query.value(1).toString();
qDebug() << id << name;
}
}
然后在widget.cpp文件中添加头文件 #include <QSqlError> 。
我们在程序中利用列表存储了同一属性的多个值,然后进行了值绑定。最后执行execBatch()函数进行批处理。注意程序中利用QVariant(QVariant::String)来输入空值NULL,因为前面都是QString类型的,所以这里要使用QVariant::String 使格式一致化。
运行效果如下:
四,事务操作。
事务是数据库的一个重要功能,所谓事务是用户定义的一个数据库操作序列,这些操作要么全做要么全不做,是一个不可分割的工作单位。在Qt中用transaction()开始一个事务操作,用commit()函数或rollback()函数进行结束。commit()表示提交,即提交事务的所有操作。具体地说就是将事务中所有对数据库的更新写回到数据库,事务正常结束。rollback()表示回滚,即在事务运行的过程中发生了某种故障,事务不能继续进行,系统将事务中对数据库的所有已完成的操作全部撤销,回滚到事务开始时的状态。
如下面的例子:
void Widget::on_pushButton_clicked()
{
if(QSqlDatabase::database().driver()->hasFeature(QSqlDriver::Transactions))
{ //先判断该数据库是否支持事务操作
QSqlQuery query;
if(QSqlDatabase::database().transaction()) //启动事务操作
{
//
//下面执行各种数据库操作
query.exec("insert into student values (14, 'hello')");
query.exec("delete from student where id = 1");
//
if(!QSqlDatabase::database().commit())
{
qDebug() << QSqlDatabase::database().lastError(); //提交
if(!QSqlDatabase::database().rollback())
qDebug() << QSqlDatabase::database().lastError(); //回滚
}
}
//输出整张表
query.exec("select * from student");
while(query.next())
qDebug() << query.value(0).toInt() << query.value(1).toString();
}
}
然后在widget.cpp文件中添加头文件 #include <QSqlDriver> 。
QSqlDatabase::database()返回程序前面所生成的连接的QSqlDatabase对象。hasFeature()函数可以查看一个数据库是否支持事务。
运行结果如下:
可以看到结果是正确的。
对SQL语句我们就介绍这么多,其实Qt中提供了更为简单的不需要SQL语句就可以操作数据库的方法,我们在下一节讲述这些内容。