QT利用QSqlQuery类执行SQL语句

参考网址:http://www.qter.org/forum.php?mod=viewthread&tid=6448

导语


        SQL即结构化查询语言,是关系数据库的标准语言。前面两节中已经在Qt里利用QSqlQuery类执行了SQL语句,这一节我们将详细讲解该类的使用。需要说明,因为我们重在讲解Qt中数据库使用,而非专业的讲解数据库知识,所以不会对数据库中的一些知识进行深入讲解。

环境:Windows 7 + Qt 5.8.0(包含Qt Creator 4.2.1)

目录


一、创建数据库连接

二、操作结果集

三、在SQL语句中使用变量

四、批处理操作

五、事务操作

 

正文


一、创建数据库连接

        前面我们是在主函数中创建数据库连接,然后打开并使用。实际中为了明了方便,一般将数据库连接单独放在一个头文件中。下面来看一个例子。

1.新建Qt Widgets应用,项目名称为myquery,基类为QMainWindow,类名为MainWindow。完成后打开myquery.pro并添加代码:QT += sql  然后保存该文件。

2.向项目中添加新文件,模板选择C++分类中的C++ Header File,名称为connection.h,然后打开该文件,更改如下:

#ifndef CONNECTION_H

#define CONNECTION_H

#include

#include

#include

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


 

        在这个头文件中我们添加了一个建立连接的函数,使用这个头文件的目的就是要简化主函数中的内容。这里先创建了一个SQLite数据库的默认连接,设置数据库名称时使用了“:memory:”,表明这个是建立在内存中的数据库,也就是说该数据库只在程序运行期间有效,等程序运行结束时就会将其销毁。当然,大家也可以将其改为一个具体的数据库名称,比如“my.db”,这样就会在项目目录中创建该数据库文件了。下面使用open()函数将数据库打开,如果打开失败,则弹出提示对话框。最后使用QSqlQuery创建了一个student表,并插入了包含id和name两个字段的五条记录,如下图所示。其中,id字段是int类型的,“primary key”表明该字段是主键,它不能为空,而且不能有重复的值;而name字段是varchar类型的,并且不大于20个字符。这里使用的SQL语句都要包含在双引号中,如果一行写不完,那么分行后,每一行都要使用两个双引号引起来。

 

 

        需要注意,代码中的query没有进行任何指定就可以操作前面打开的数据库,这是因为现在只有一个数据库连接,它就是默认连接,这时候所有的操作都是针对该连接的。但是如果要同时操作多个数据库连接,就需要进行指定了,这方面内容可以参考《Qt Creator快速入门》的第17章相关内容。


 

3.下面我们到main.cpp中调用连接函数。

 

#include "mainwindow.h"

#include

#include "connection.h"

int main(int argc, char *argv[])

{

    QApplication a(argc, argv);

    if (!createConnection())

            return 1;

    MainWindow w;

    w.show();

    return a.exec();

}


 

4.我们往界面上添加一个按钮来实现查询操作。双击mainwindow.ui文件进入设计模式。然后将一个Push Button拖到界面上,并修改其显示文本为“查询”。效果如下图所示。
 

5.在查询按钮上点击鼠标右键,选择“转到槽”,然后选择clicked()单击信号槽并点击OK,如下图所示。

6.将槽的内容更改如下:

void MainWindow::on_pushButton_clicked()

{

    QSqlQuery query;

    query.exec("select * from student");

    while(query.next())

    {

        qDebug() << query.value(0).toInt()

                       << query.value(1).toString();

    }

}

7.在mainwindow.cpp文件中添加头文件:

#include

#include
 

8.运行程序,然后按下查询按钮,在应用程序输出窗口将会输出结果,效果如下图所示。

二、操作结果集

    在前面的程序中,我们使用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 MainWindow::on_pushButton_clicked()

{

    QSqlQuery query;

    query.exec("select * from student");

    qDebug() << "exec next() :";

    //开始就先执行一次next()函数,那么query指向结果集的第一条记录

    if(query.next())

    {

        //获取query所指向的记录在结果集中的编号

        int rowNum = query.at();

        //获取每条记录中字段(即列)的个数

        int columnNum = query.record().count();

        //获取"name"字段所在列的编号,列从左向右编号,最左边的编号为0

        int fieldNo = query.record().indexOf("name");

        //获取id字段的值,并转换为int型

        int id = query.value(0).toInt();

        //获取name字段的值

        QString name = query.value(fieldNo).toString();

        //将结果输出

        qDebug() << "rowNum is : " << rowNum

                 << " id is : " << id

                 << " name is : " << name

                 << " columnNum is : " << columnNum;

    }

    //定位到结果集中编号为2的记录,即第三条记录,因为第一条记录的编号为0

    qDebug() << "exec seek(2) :";

    if(query.seek(2))

    {

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

    }

}

最后在mainwindow.cpp中添加#include 头文件包含,运行程序,点击查询按钮,输出结果如下图所示。

三、在SQL语句中使用变量

1.我们先来看一个例子。首先在设计模式往界面上添加一个Spin Box部件,如下图所示。

2.将查询按钮槽里面的内容更改如下:

void MainWindow::on_pushButton_clicked()

{

    QSqlQuery query;

    int id = ui->spinBox->value();

    query.exec(QString("select name from student where id = %1")

               .arg(id));

    query.next();

    QString name = query.value(0).toString();

    qDebug() << name;

}

        这里使用了QString类的arg()函数实现了在SQL语句中使用变量,我们运行程序,更改Spin Box的值,然后点击查询按钮,效果如下图所示。

3.其实在QSqlQuery类中提供了数据绑定同样可以实现在SQL语句中使用变量,虽然它也是通过占位符来实现的,不过使用它形式上更明了一些。下面先来看一个例子,将查询按钮槽更改如下:

void MainWindow::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 。

特别注意,在最后一定要执行exec()函数,所做的操作才能被真正执行。运行程序,点击查询按钮,可以看到前面添加的记录的信息。这里的“: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();

以上各种形式的表示方式效果是一样的。

4.下面我们演示一下通过绑定操作在SQL语句中使用变量。更改槽函数如下:

void MainWindow::on_pushButton_clicked()

{

    QSqlQuery query;

    query.prepare("select name from student where id = ?");

    int id = ui->spinBox->value();

    query.addBindValue(id);

    query.exec();

    query.next();

    qDebug() << query.value(0).toString();

}

        运行程序,可以实现通过Spin Box的值来进行查询。

四、批处理操作

        当要进行多条记录的操作时,我们就可以利用绑定进行批处理。将槽更改如下:

void MainWindow::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;

    }

}

        然后需要在mainwindow.cpp上添加头文件包含:#include 。我们在程序中利用列表存储了同一字段的多个值,然后进行了值绑定。最后执行execBatch()函数进行批处理。注意程序中利用QVariant(QVariant::String)来输入空值NULL,因为前面都是QString类型的,所以这里要使用QVariant::String 使格式一致化。

运行程序,效果如下图所示:

五、事务操作

        事务可以保证一个复杂的操作的原子性,就是对于一个数据库操作序列,这些操作要么全部做完,要么一条也不做,它是一个不可分割的工作单位。在Qt中,如果底层的数据库引擎支持事务,那么QSqlDriver::hasFeature(QSqlDriver::Transactions)会返回true。可以使用QSqlDatabase::transaction()来启动一个事务,然后编写一些希望在事务中执行的SQL语句,最后调用QSqlDatabase::commit()或者QSqlDatabase::rollback()。当使用事务时必须在创建查询以前就开始事务,例如:

QSqlDatabase::database().transaction();

QSqlQuery query;

query.exec("SELECT id FROM employee WHERE name = 'Torild Halvorsen'");

if (query.next()) {

    int employeeId = query.value(0).toInt();

    query.exec("INSERT INTO project (id, name, ownerid) "

               "VALUES (201, 'Manhattan Project', "

               + QString::number(employeeId) + ')');

}

QSqlDatabase::database().commit();

结语


        对执行SQL语句我们就介绍这么多,其实Qt中提供了更为简单的不需要SQL语句就可以操作数据库的方法,我们在下一篇讲述这些内容。

你可能感兴趣的:(SQL,Qt)