QSqlQuery類提供了一個用於執行SQL語句和瀏覽查詢的結果集的接口。
QSqlQueryModel和QSqlTableModel類提供了一個用於訪問數據庫的高級接口,這將在下一節介紹。如果你不熟悉SQL,你可以直接使用下一節介紹的高級接口類。
1 執行一個查詢
要執行一個SQL語句,簡單的創建一個QSqlQuery對象,然后調用QSqlQuery::exec()方法即可,如下所示:
QSqlQuery query; query.exec("SELECT name, salary FROM employee WHERE salary > 50000");
QSqlQuery的構造函數接受一個可選的QSqlDatabase對象參數,用來指定將使用哪一個數據庫連接。在上面的例子中,我們並沒有指定任何連接,因此使用默認連接。如果執行查詢時出錯,exec()將返回false,錯誤原因可以通過QSqlQuery::lastError()查看。
2 瀏覽查詢結果
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()函數返回當前記錄的一個字段值。字段的索引號從0開始計數。QSqlQuery::value()返回一個QVariant,這個類型可以保存各種C++和核心的Qt數據類型,比如int, QString和QByteArray等。不同的數據庫類型將自動地映射到最近的Qt等價類型。在上面的代碼中,我們使用了QVariant::toString()和QVariant::toInt()將變量類型轉換為了QString和int。
補充:關於value()方法的參數-索引號的取值范圍問題,我們只知道第一個字段對應的索引號為0,但是並不知道最后一個字段的索引號的時候,我們可以遍歷所有可能值,由於當value()函數的索引號非法時,會返回一個非法的QVariant變量,因此,我們可以通過判斷返回的QVariant變量的合法性來得知是否已經到了最后一個字段。下面給出一段示例代碼:
while (query.next()){ int i = 0; QString result; QVariant temp; while ((temp = query.value(i++)).isValid()){ result += temp.toString(); result += " | "; } ui->textBrowser->append(result); }
下面給出一個MYSQL與Qt支持的數據類型之間的轉換關系表:
SQLITE3與Qt支持的數據類型之間的轉換關系表如下:
你可以使用QSqlQuery::next(),QSqlQuery::previous(), QSqlQuery::first()和QSqlQuery::last()來前后移動指向記錄位置的指針。可以使用QSqlQuery::at()返回當前行的索引號,如果對應的數據庫支持,可以使用QSqlQuery::size()返回查詢結果集的總的行數(MYSQL是支持的)。
要驗證一個數據庫驅動是否支持某一個特性,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::setForwartOnly(true)。當你在操作大量結果集的時候,這是一個簡單的優化單可以明顯加速你的查詢。
3 插入、更新和刪除記錄
QSqlQuery可以執行任意的SQL語句。下面的示例展示了使用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提供的所有數據庫驅動都支持。當需要插入多條記錄時,只需要調用 一次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");
4 事務
如果底層的數據庫引擎支持事務,那么QSqlDriver::hasFeature(QSqlDriver::Transactions)會返回true。可以使用QSqlDatabase::transaction()啟動一個事務,然后編寫一些希望在事務中執行的SQL語句,最后調用QSqlDatabase::commit()或者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();
事務可以保證一個復雜操作的原子性,就是對於一個數據庫操作序列,這些操作要么全部做完,要么一條也不做,它是一個不可分割的工作單位。