【Qt筆記】數據庫操做

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 語法),咱們能夠選擇QSqlTableModelQSqlRelationalTableModel。本章咱們介紹QSqlQuery類,在後面的章節則介紹QSqlTableModelQSqlRelationalTableModel安全

在使用時,咱們能夠經過服務器

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()函數則能夠根據名字獲取所須要的數據庫鏈接。

相關文章
相關標籤/搜索