Qt 提供了 QtSql 模塊來提供平臺獨立的基於 SQL 的數據庫操做。這裏咱們所說的「平臺獨立」,既包括操做系統平臺,有包括各個數據庫平臺。另外,咱們強調了「基於 SQL」,由於 NoSQL 數據庫至今沒有一個通用查詢方法,因此不可能提供一種通用的 NoSQL 數據庫的操做。Qt 的數據庫操做還能夠很方便的與 model/view 架構進行整合。一般來講,咱們對數據庫的操做更多地在於對數據庫表的操做,而這正是 model/view 架構的長項。sql
Qt 使用QSqlDatabase
表示一個數據庫鏈接。更底層上,Qt 使用驅動(drivers)來與不一樣的數據庫 API 進行交互。Qt 桌面版本提供了以下幾種驅動:數據庫
驅動 | 數據庫 |
QDB2 | IBM DB2 (7.1 或更新版本) |
QIBASE | Borland InterBase |
QMYSQL | MySQL |
QOCI | Oracle Call Interface Driver |
QODBC | Open Database Connectivity (ODBC) – Microsoft SQL Server 及其它兼容 ODBC 的數據庫 |
QPSQL | PostgreSQL (7.3 或更新版本) |
QSQLITE2 | SQLite 2 |
QSQLITE | SQLite 3 |
QSYMSQL | 針對 Symbian 平臺的SQLite 3 |
QTDS | Sybase Adaptive Server (自 Qt 4.7 起廢除) |
若是習慣於使用 SQL 語句,咱們能夠選擇QSqlQuery
類;若是隻須要使用高層次的數據庫接口(不關心 SQL 語法),咱們能夠選擇QSqlTableModel
和QSqlRelationalTableModel
。本章咱們介紹QSqlQuery
類,在後面的章節則介紹QSqlTableModel
和QSqlRelationalTableModel
。安全
在使用時,咱們能夠經過服務器
QSqlDatabase::drivers();
找到系統中全部可用的數據庫驅動的名字列表。咱們只能使用出如今列表中的驅動。因爲默認狀況下,QtSql 是做爲 Qt 的一個模塊提供的。爲了使用有關數據庫的類,咱們必須早 .pro 文件中添加這麼一句:架構
QT += sql
這表示,咱們的程序須要使用 Qt 的 core、gui 以及 sql 三個模塊。注意,若是須要同時使用 Qt4 和 Qt5 編譯程序,一般咱們的 .pro 文件是這樣的:函數
QT += core gui sql greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
這兩句也很明確:Qt 須要加載 core、gui 和 sql 三個模塊,若是主板本大於 4,則再添加 widgets 模塊。性能
下面來看一個簡單的函數:ui
bool connect(const QString &dbName) { QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE"); // db.setHostName("host"); // db.setDatabaseName("dbname"); // db.setUserName("username"); // db.setPassword("password"); db.setDatabaseName(dbName); if (!db.open()) { QMessageBox::critical(0, QObject::tr("Database Error"), db.lastError().text()); return false; } return true; }
咱們使用connect()
函數建立一個數據庫鏈接。咱們使用QSqlDatabase::addDatabase()
靜態函數完成這一請求,也就是建立了一個QSqlDatabase
實例。注意,數據庫鏈接使用本身的名字進行區分,而不是數據庫的名字。例如,咱們可使用下面的語句:操作系統
QSqlDatabase db=QSqlDatabase::addDatabase("QSQLITE", QString("con%1").arg(dbName));
此時,咱們是使用addDatabase()
函數的第二個參數來給這個數據庫鏈接一個名字。在這個例子中,用於區分這個數據庫鏈接的名字是QString("conn%1").arg(dbName)
,而不是 「QSQLITE」。這個參數是可選的,若是不指定,系統會給出一個默認的名字QSqlDatabase::defaultConnection
,此時,Qt 會建立一個默認的鏈接。若是你給出的名字與已存在的名字相同,新的鏈接會替換掉已有的鏈接。經過這種設計,咱們能夠爲一個數據庫創建多個鏈接。設計
咱們這裏使用的是 sqlite 數據庫,只須要指定數據庫名字便可。若是是數據庫服務器,好比 MySQL,咱們還須要指定主機名、端口號、用戶名和密碼,這些語句使用註釋進行了簡單的說明。
接下來咱們調用了QSqlDatabase::open()
函數,打開這個數據庫鏈接。經過檢查open()
函數的返回值,咱們能夠判斷數據庫是否是正確打開。
QtSql 模塊中的類大多具備lastError()
函數,用於檢查最新出現的錯誤。若是你發現數據庫操做有任何問題,應該使用這個函數進行錯誤的檢查。這一點咱們也在上面的代碼中進行了體現。固然,這只是最簡單的實現,通常來講,更好的設計是,不要在數據庫操做中混雜界面代碼(而且將這個connect()
函數放在一個專門的數據庫操做類中)。
接下來咱們能夠在main()
函數中使用這個connect()
函數:
int main(int argc, char *argv[]) { QApplication a(argc, argv); if (connect("demo.db")) { QSqlQuery query; if (!query.exec("CREATE TABLE student (" "id INTEGER PRIMARY KEY AUTOINCREMENT," "name VARCHAR," "age INT)")) { QMessageBox::critical(0, QObject::tr("Database Error"), query.lastError().text()); return 1; } } else { return 1; } return a.exec(); }
main()
函數中,咱們調用這個connect()
函數打開數據庫。若是打開成功,咱們經過一個QSqlQuery
實例執行了 SQL 語句。一樣,咱們使用其lastError()
函數檢查了執行結果是否正確。
注意這裏的QSqlQuery
實例的建立。咱們並無指定是爲哪個數據庫鏈接建立查詢對象,此時,系統會使用默認的鏈接,也就是使用沒有第二個參數的addDatabase()
函數建立的那個鏈接(其實就是名字爲QSqlDatabase::defaultConnection
的默認鏈接)。若是沒有這麼一個鏈接,系統就會報錯。也就是說,若是沒有默認鏈接,咱們在建立QSqlQuery
對象時必須指明是哪個QSqlDatabase
對象,也就是addDatabase()
的返回值。
咱們還能夠經過使用QSqlQuery::isActive()
函數檢查語句執行正確與否。若是QSqlQuery
對象是活動的,該函數返回 true。所謂「活動」,就是指該對象成功執行了exec()
函數,可是尚未完成。若是須要設置爲不活動的,可使用finish()
或者clear()
函數,或者直接釋放掉這個QSqlQuery
對象。這裏須要注意的是,若是存在一個活動的 SELECT 語句,某些數據庫系統不能成功完成connect()
或者rollback()
函數的調用。此時,咱們必須首先將活動的 SELECT 語句設置成不活動的。
建立過數據庫表 student 以後,咱們開始插入數據,而後將其獨取出來:
if (connect("demo.db")) { QSqlQuery query; query.prepare("INSERT INTO student (name, age) VALUES (?, ?)"); QVariantList names; names << "Tom" << "Jack" << "Jane" << "Jerry"; query.addBindValue(names); QVariantList ages; ages << 20 << 23 << 22 << 25; query.addBindValue(ages); if (!query.execBatch()) { QMessageBox::critical(0, QObject::tr("Database Error"), query.lastError().text()); } query.finish(); query.exec("SELECT name, age FROM student"); while (query.next()) { QString name = query.value(0).toString(); int age = query.value(1).toInt(); qDebug() << name << ": " << age; } } else { return 1; }
依舊鏈接到咱們建立的 demo.db 數據庫。咱們須要插入多條數據,此時可使用QSqlQuery::exec()
函數一條一條插入數據,可是這裏咱們選擇了另一種方法:批量執行。首先,咱們使用QSqlQuery::prepare()
函數對這條 SQL 語句進行預處理,問號 ? 至關於佔位符,預示着之後咱們可使用實際數據替換這些位置。簡單說明一下,預處理是數據庫提供的一種特性,它會將 SQL 語句進行編譯,性能和安全性都要優於普通的 SQL 處理。在上面的代碼中,咱們使用一個字符串列表 names 替換掉第一個問號的位置,一個整型列表 ages 替換掉第二個問號的位置,利用QSqlQuery::addBindValue()
咱們將實際數據綁定到這個預處理的 SQL 語句上。須要注意的是,names 和 ages 這兩個列表裏面的數據須要一一對應。而後咱們調用QSqlQuery::execBatch()
批量執行 SQL,以後結束該對象。這樣,插入操做便完成了。
另外說明一點,咱們這裏使用了 ODBC 風格的 ? 佔位符,一樣,咱們也可使用 Oracle 風格的佔位符:
query.prepare("INSERT INTO student (name, age) VALUES (:name, :age)");
此時,咱們就須要使用
query.bindValue(":name", names); query.bindValue(":age", ages);
進行綁定。Oracle 風格的綁定最大的好處是,綁定的名字和值很清晰,與順序無關。可是這裏須要注意,bindValue()
函數只能綁定一個位置。好比
query.prepare("INSERT INTO test (name1, name2) VALUES (:name, :name)"); // ... query.bindValue(":name", name);
只能綁定第一個 :name 佔位符,不能綁定到第二個。
接下來咱們依舊使用同一個查詢對象執行一個 SELECT 語句。若是存在查詢結果,QSqlQuery::next()
會返回 true,直到到達結果最末,返回 false,說明遍歷結束。咱們利用這一點,使用 while 循環便可遍歷查詢結果。使用QSqlQuery::value()
函數便可按照 SELECT 語句的字段順序獲取到對應的數據庫存儲的數據。
對於數據庫事務的操做,咱們可使用 QSqlDatabase::transaction()
開啓事務,QSqlDatabase::commit()
或者QSqlDatabase::rollback()
結束事務。使用QSqlDatabase::database()
函數則能夠根據名字獲取所須要的數據庫鏈接。