十五、Qt數據庫 之 利用QSqlQuery類執行SQL語句

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語句就可以操作數據庫的方法,我們在下一節講述這些內容。

發佈了31 篇原創文章 · 獲贊 4 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章