346
技術社區[雲棲]
[Qt教程] 第23篇 數據庫(三)利用QSqlQuery類執行SQL語句
樓主
發表於 2013-5-15 22:39:29 | 查看:
813| 回複: 0

利用QSqlQuery類執行SQL語句
版權聲明
該文章原創於作者yafeilinux,轉載請注明出處!
導語
SQL即結構化查詢語言,是關係數據庫的標準語言。前麵兩節中已經在Qt裏利用QSqlQuery類執行了SQL語句,這一節我們將詳細講解該類的使用。需要說明,因為我們重在講解Qt中的數據庫使用,而非專業的講解數據庫知識,所以不會對數據庫中的一些知識進行深入講解。
環境:Windows Xp + Qt 4.8.4+Qt Creator2.6.2
目錄
一、創建數據庫連接
二、操作結果集
三、在SQL語句中使用變量
四、批處理操作
五、事務操作
正文
一、創建數據庫連接
前麵我們是在主函數中創建數據庫連接,然後打開並使用。實際中為了明了方便,一般將數據庫連接單獨放在一個頭文件中。下麵來看一個例子。
1.新建Qt Gui應用,項目名稱為myquery,基類為QMainWindow,類名為MainWindow。完成後打開myquery.pro並將第一行代碼更改為:
QT += coregui sql
然後保存該文件。
2.向項目中添加新的C++頭文件,名稱為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 establisha 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 <QApplication>
#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()單擊信號槽並點擊確定,如下圖所示。
![]() 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 <QSqlQuery>
#include <QDebug>
8.運行程序,然後按下查詢按鈕,在應用程序輸出窗口將會輸出結果,效果如下圖所示。
![]() 二、操作結果集
在前麵的程序中,我們使用query.exec("select * from student");查詢出表中所有的內容。其中的SQL語句“select * from student”中“*”號表明查詢表中記錄的所有屬性。而當query.exec("select * from student");這條語句執行完後,我們便獲得了相應的執行結果,因為獲得的結果可能不止一條記錄,所以稱之為結果集。
結果集其實就是查詢到的所有記錄的集合,在QSqlQuery類中提供了多個函數來操作這個集合,需要注意這個集合中的記錄是從0開始編號的。最常用的操作有:
需要特別注意,剛執行完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 <QSqlRecord>頭文件包含,運行程序,點擊查詢按鈕,輸出結果如下圖所示。
![]() 三、在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語句中使用變量,雖然它也是通過占位符來實現的,不過使用它形式上更明了一些。下麵先來看一個例子,將查詢按鈕槽更改如下:
這裏在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 <QSqlError>。我們在程序中利用列表存儲了同一屬性的多個值,然後進行了值綁定。最後執行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 FROMemployee WHERE name = 'Torild Halvorsen'");
if (query.next())
{
int employeeId
= query.value(0).toInt();
query.exec("INSERT
INTO project(id, name, ownerid) "
"VALUES
(201, 'ManhattanProject', "
+ QString::number(employeeId)
+ ')');
}
QSqlDatabase::database().commit();
結語
對執行SQL語句我們就介紹這麼多,其實Qt中提供了更為簡單的不需要SQL語句就可以操作數據庫的方法,我們在下一節講述這些內容。
涉及到的源碼: ![]() ![]() |
最後更新:2017-04-03 14:54:11