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