Qt數據庫開發指南【譯自Qt幫助文檔】

1、數據庫連接

爲使用QSqlQueryQSqlQueryModel訪問數據庫,需要創建並打開一個或多個數據庫連接。數據庫連接通常通過連接名稱來標識。可以建立同一個數據庫的多個連接。QSqlDatabase支持一個未命名的默認連接。當調用需要以連接名稱爲參數的QSqlQuery或者QSqlQueryModel成員函數時,如果你不傳遞連接名稱,將使用默認連接。當應用只使用一個數據庫連接時,創建一個默認連接非常方便。

注意創建連接和打開連接的區別。創建一個連接包括創建一個QSqlDatabase的實例。連接直到打開它時纔可用。下面的示例展示瞭如何創建一個默認連接並打開它。

     QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL");

     db.setHostName("bigblue");

     db.setDatabaseName("flightdb");

     db.setUserName("acarlson");

     db.setPassword("1uTbSbAs");

     bool ok = db.open();

第一行代碼創建了連接對象。最後一行代碼打開了連接。在中間的代碼裏,我們初始化了一些連接參數,包括數據庫名稱、主機名稱、密碼。此時,我們連接了名爲bigblue的主機上的一個MYSQL數據庫。addDatabase()的“QMYSQL”參數指定了連接使用的數據庫驅動類型。Qt支持的數據庫驅動類型見表suported database drivers.

示例代碼中的連接爲默認連接。因爲我們沒有指定addDatabase()函數的第二個參數,該參數指定了連接名稱。例如下面的代碼中,我們創建了兩個MYSQL數據庫連接分別名爲“first”和“second”。

    QSqlDatabase firstDB = QSqlDatabase::addDatabase("QMYSQL", "first");

     QSqlDatabase secondDB = QSqlDatabase::addDatabase("QMYSQL", "second");

當連接初始化之後,使用open()函數創建兩個活動的連接。如果open()函數執行失敗,將返回false。此時,可以使用QSqlDatabase::lastError()獲取錯誤信息。

一旦連接創建,我們可以使用連接名稱爲參數調用靜態函數QSqlDatabase::database()獲取數據庫連接的指針。如果不傳遞連接名稱參數,將返回默認連接。例如:

     QSqlDatabase defaultDB = QSqlDatabase::database();

     QSqlDatabase firstDB = QSqlDatabase::database("first");

     QSqlDatabase secondDB = QSqlDatabase::database("second");

爲移除一個數據庫連接,可以使用QSqlDatabase::close()關閉數據庫並使用靜態方法QSqlDatabase::removeDatabase()移除連接。

 

2、執行SQL語句

QSqlQuery類提供接口用於執行SQL語句並瀏覽查詢的結果集。

下一節要介紹的QSqlQueryModelQSqlTableModel類提供了訪問數據庫的高級接口。如果你對SQL不熟悉,你可以直接跳到下一節。

執行查詢

爲執行一個SQL語句,只需創建一個QSqlQuery對象並調用QSqlQuery::exec()

QSqlQuery query;

query.exec(“SELECT name,salary FROM employee WHERE salary > 50000”);

QSqlQuery構造函數接受一個可選的QSqlDatabase對象作爲參數來要使用的數據庫連接。在上例中,我們沒有指定,它將使用默認數據庫。

如果發生錯誤,exec()將返回false。錯誤的詳細信息可以通過QSqlQuery::lastError()獲取。

訪問結果集

QSqlQuery提供每次訪問結果集的一個記錄。在exec()調用後,QSqlQuery的內部指針將指向第一個記錄的前一個位置。我們必須調用一次QSqlQuery::next()來前進到第一個記錄。重複調用next()將可以逐個訪問餘下的記錄直到返回false。下面是一段示例代碼:

     while (query.next()) {

         QString name = query.value(0).toString();

         int salary = query.value(1).toInt();

         qDebug() << name << salary;

     }

QSqlQuery::value()返回當前記錄一個段的值。段由一個非零索引來指定。QSqlQuery::value()返回一個QVariant。不同的數據庫數據類型可以自動映射到與Qt環境最接近的數據類型。

可以使用QSqlQuery::next()QSqlQuery::previous()QSqlQuery::first()QSqlQuery::last()QSqlQuery::seek()對數據集進行遍歷。可以使用QSqlQuery::at()獲取當前行的索引值。可以使用QSqlQuery::size()獲取數據集的行數。

爲檢測數據庫驅動是否支持某個特性,可以使用QSqlDriver::hasFeature()。在下面的例子中,我們調用QSqlQuery::size()檢查了當前數據庫支持特性的結果集。我們訪問了最後一個記錄並使用查詢的位置得知記錄的行數。

     QSqlQuery query;

     int numRows;

     query.exec("SELECT name, salary FROM employee WHERE salary > 50000");

 

     QSqlDatabase defaultDB = QSqlDatabase::database();

     if (defaultDB.driver()->hasFeature(QSqlDriver::QuerySize)) {

         numRows = query.size();

     } else {

         // this can be very slow

         query.last();

         numRows = query.at() + 1;

     }

如果只使用正數爲參數通過next()seek()訪問結果集,你可以在調用exec()之前調用QSqlQuery::setForwardOnly(true)。這樣可以優化查詢的速度。

插入,更新,刪除記錄

QSqlQuery可以執行任意的SQL語句,而不只是SELECT。下面是使用INSERT在一個表中插入一個記錄。

     QSqlQuery query;

     query.exec("INSERT INTO employee (id, name, salary) "

                "VALUES (1001, 'Thad Beaumont', 65000)");

如果你希望一次插入多個記錄,將查詢語句和需要操作的實際數據值分開可以提高效率。可以使用佔位符來實現。Qt支持兩種佔位符語法:名稱綁定和位置綁定。

下例爲名稱綁定:

     QSqlQuery query;

     query.prepare("INSERT INTO employee (id, name, salary) "

                   "VALUES (:id, :name, :salary)");

     query.bindValue(":id", 1001);

     query.bindValue(":name", "Thad Beaumont");

     query.bindValue(":salary", 65000);

     query.exec();

下例爲位置綁定:

     QSqlQuery query;

     query.prepare("INSERT INTO employee (id, name, salary) "

                   "VALUES (?, ?, ?)");

     query.addBindValue(1001);

     query.addBindValue("Thad Beaumont");

     query.addBindValue(65000);

     query.exec();

兩種語法都可以用於Qt支持的所有數據庫驅動。如果數據庫原生地支持這種語法,Qt將只是簡單地把查詢送到DBMS。否則,Qt將對查詢進行預處理來模擬佔位符語法。在被DBMS執行後結束的實際的查詢將會像QSqlQuery::executedQuery()一樣可用。

當插入多個記錄時,只需要調用一次QSqlQuery::prepare()。可以多次調用bindValue()addBindValue()添加記錄行,之後調用exec()插入多個記錄。

除了性能之外,佔位符的另一個優點是你可以指定任意的值而不用擔心規避特殊字符。

更新記錄和插入記錄一樣簡單:

     QSqlQuery query;

     query.exec("UPDATE employee SET salary = 70000 WHERE id = 1003");

你可以使用名稱綁定或者位置綁定來指定數據值。

最後是一個刪除語句。

     QSqlQuery query;

     query.exec("DELETE FROM employee WHERE id = 1007");

事務

如果當前的數據庫支持事務,QSqlDriver::hasFeature(QSqlDriver::Transactions)將返回true。你可以使用QSqlDatabase::transaction()來初始化一個事務。接下來可以使用需要執行的事務的SQL命令,然後執行QSqlDatabase::commint()或者QSqlDatabase::rollback()。當使用事務時,在創建查詢之前必須啓動事務。

     QSqlDatabase::database().transaction();

     QSqlQuery query;

     query.exec("SELECT id FROM employee WHERE name = 'Torild Halvorsen'");

     if (query.next()) {

         int employeeId = query.value(0).toInt();

         query.exec("INSERT INTO project (id, name, ownerid) "

                    "VALUES (201, 'Manhattan Project', "

                    + QString::number(employeeId) + ')');

     }

     QSqlDatabase::database().commit();

事務可以用來確保一個複雜操作的原子執行,或者提供一種在執行中取消一個複雜操作的方法。

3、使用SQL Model

除了QSqlQueryQt提供了3種高級的類用於訪問數據庫:QSqlQueryModelQSqlTableModelQSqlRelationalTableModel

QSqlQueryModel:基於任意SQL查詢的一個只讀模式。

QSqlTableModel:工作於一個單獨表的讀寫模式。

QSqlRelationalTableModel:一個支持外鍵的QSqlTableModel子類。

這些類繼承自QAbstractTableModel,可以方便地將數據庫中的數據在一個項視圖類如QListViewQTableView中展現。

使用這些類的另一個優點是可以使代碼更簡單地適用於其他的數據源。例如,如果你使用QSqlTableModel,之後決定使用XML文件來存儲數據代替數據庫,將之需要替換數據模型即可。

SQL Query Model

QSqlQueryModel提供一個基於SQL查詢的只讀模式。

例如:

     QSqlQueryModel model;

     model.setQuery("SELECT * FROM employee");

 

     for (int i = 0; i < model.rowCount(); ++i) {

         int id = model.record(i).value("id").toInt();

         QString name = model.record(i).value("name").toString();

         qDebug() << id << name;

     }

QSqlTableModel

QSqlTableModel是一個高級的選擇用於瀏覽和更改獨立的SQL表。需要更少的代碼,並基本不需要SQL語法的知識。

使用QSqlTableModel::record()來獲取表中的一行。QSqlTableModel::setRecord()可以更改一行。例如,如下代碼將會使每個成員的工資增加10%.

     for (int i = 0; i < model.rowCount(); ++i) {

         QSqlRecord record = model.record(i);

         double salary = record.value("salary").toInt();

         salary *= 1.1;

         record.setValue("salary", salary);

         model.setRecord(i, record);

     }

     model.submitAll();

可以使用QSqlTableModel::data()QSqlTableModel::setData()來訪問數據。例如,下面是一個使用setData()更新記錄的例子。

     model.setData(model.index(row, column), 75000);

     model.submitAll();

下面是一個插入行並填充數據的例子:

     model.insertRows(row, 1);

     model.setData(model.index(row, 0), 1013);

     model.setData(model.index(row, 1), "Peter Gordon");

     model.setData(model.index(row, 2), 68500);

     model.submitAll();

下面是一個刪除5個連續行的例子:

     model.removeRows(row, 5);

     model.submitAll();

QSqlTableModel::removeRows()的第一個參數是要刪除的數據行的第一行的索引。

當完成更改記錄時,可以使用QSqlTableModel::submitAll()來確保更改被寫入到數據庫中。

何時調用和是否需要調用submitAll()取決於數據表的編輯策略。默認的策略是QSqlTableModel::OnRowChange,指定了當用戶選擇了一個不同行時執行對數據行的更改。其他策略QSqlTableModel::OnManualSubmint(當調用submintAll()時執行更改)、QSqlTableModel::OnFieldCHange(不對更改進行緩存)。

QSqlTableModel::OnFieldChange看起來像是不需要用戶執行submitAll()。它有兩個缺陷:

沒有緩存,性能將會明顯下降。

如果更改了主鍵,記錄將會在你試圖填充時丟失。

SQL Relational Table Model

QSqlRelationalTableModel擴展了QSqlTableModel,它支持外鍵。外鍵是一個表中的字段與另一個表的主鍵字段之間的一對一映射。例如:如果一個book表有一個稱爲authorid的字段標識了author表的id字段,我們稱authorid爲一個外鍵。

 

 

上方的截圖在一個QTableView中顯示了一個QSqlTableModel。外鍵(citycountry)不能被直接解讀。下方的截圖中顯示了一個QSqlRelationalTableModel,外鍵被解析爲可直接解讀的字符串。

     model->setTable("employee");

 

     model->setRelation(2, QSqlRelation("city", "id", "name"));

     model->setRelation(3, QSqlRelation("country", "id", "name"));

關於QSqlRelationalTableModel::setRelation(int column,const QSqlRelation &relation)的解釋:

功能:設置表的column列爲外鍵,外鍵關係爲relation

QSqlRelation(const QString & tableName, const QString & indexColumn, const QString & displayColumn)

tableName,參照表的名稱

indexColumn,索引列

displayColunm,顯示列


4、在表視圖中顯示數據

QSqlQueryModelQSqlTableModelQSqlRelationalTableModel類可以用作Qt視圖類(如QListView, QTableView和 QTreeView)的數據源。通常使用QTableView,因爲SQL結果集通常爲二維數據結構。

下面是創建一個基於SQL數據模型的視圖:

     QTableView *view = new QTableView;

     view->setModel(model);

     view->show();

如果模型是讀寫模型,視圖將允許用戶編輯字段內容。可以通過調用  view->setEditTriggers(QAbstractItemView::NoEditTriggers);禁用編輯。

可以將相同的模型作爲多個視圖的數據源。如果用戶在一個視圖中編輯了模型,其他視圖中的數據也將立即改變。

視圖類在表的頂部顯示了列名稱。爲改變表頭的文字,可以調用setHeaderData()。默認的表頭爲表的字段名稱。例如:

     model->setHeaderData(0, Qt::Horizontal, QObject::tr("ID"));

     model->setHeaderData(1, Qt::Horizontal, QObject::tr("Name"));

     model->setHeaderData(2, Qt::Horizontal, QObject::tr("City"));

     model->setHeaderData(3, Qt::Horizontal, QObject::tr("Country"));

QTableView在左邊還有一個垂直的表頭,用數字標識數據行。如果使用QSqlTableModel::InsertRows()代碼插入了一個數據行,新的數據行將會被標記爲“*”,直到使用submitAll()更新數據,或當用戶移動到另一行。

相似的,如果使用removeRows()移除了某行,行將標記爲“!”直到更改被接受。

視圖中的項使用一個託管進行處理。默認的託管QItemDelegate可以處理大多數數據類型。當用戶開始在視圖中編輯一個項時,託管負責提供編輯窗體。你可以通過子類QAbstractItemDelegateQItemDelegate創建自己的託管。參考模型/視圖編程獲取更多信息。

QSqlTableModel對一次操作一個單獨的表的操作進行了優化。如果你需要一個讀寫模型操作任意的結果集,你可以創建一個QSqlQueryModel的子類並重載flags()setData()來使它可讀寫。下面的例子實現了使一個查詢模型的字段1和字段2可編輯。

 Qt::ItemFlags EditableSqlModel::flags(

         const QModelIndex &index) const

 {

     Qt::ItemFlags flags = QSqlQueryModel::flags(index);

     if (index.column() == 1 || index.column() == 2)

         flags |= Qt::ItemIsEditable;

     return flags;

 }

 

 bool EditableSqlModel::setData(const QModelIndex &index, const QVariant &value, int /* role */)

 {

     if (index.column() < 1 || index.column() > 2)

         return false;

 

     QModelIndex primaryKeyIndex = QSqlQueryModel::index(index.row(), 0);

     int id = data(primaryKeyIndex).toInt();

 

     clear();

 

     bool ok;

     if (index.column() == 1) {

         ok = setFirstName(id, value.toString());

     } else {

         ok = setLastName(id, value.toString());

     }

     refresh();

     return ok;

 }

setFirstName()幫助函數如下定義:

 bool EditableSqlModel::setFirstName(int personId, const QString &firstName)

 {

     QSqlQuery query;

     query.prepare("update person set firstname = ? where id = ?");

     query.addBindValue(firstName);

     query.addBindValue(personId);

     return query.exec();

 }

setLastName()函數也類似。

可以通過多種方式子類化一個模型:可以提供數據項的tooltips,更改背景顏色,提供數據計算,提供不同的值用於顯示和編輯,處理空值等等。

如果你的需求只是解析一個外鍵,可以使用QSqlRelationalTableModel。最好的方法是同時使用QSqlRelationalDelegate,提供外鍵的組合編輯框。

 


 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章