該概覽假設(shè)你有SQL的基礎(chǔ)知識,如select,insert,update和delete。盡管QSqlTableModel類在不需要SQL知識的情況下提供了瀏覽和編輯數(shù)據(jù)庫的接口,但仍強烈推薦理解SQL。
話題包括:
1.Database Classes:數(shù)據(jù)庫類
2.Connecting to Databases:連接到數(shù)據(jù)庫
????????SQL Database Drivers:SQL數(shù)據(jù)庫驅(qū)動器
3.Executing SQL Statements:執(zhí)行SQL語句
????????Data Types for Qt-supported Database Systems:Qt支持的數(shù)據(jù)庫系統(tǒng)的數(shù)據(jù)類型
4.Using the SQL Model Classes:使用SQL model類
5.Presenting Data in a Table View:在Table View中展示數(shù)據(jù)
6.Creating Data-Aware Forms:創(chuàng)建數(shù)據(jù)感知的Forms
一、Database Classes:數(shù)據(jù)庫類

SQL類分為三層:
1)Driver層:該層為SQL API層和數(shù)據(jù)庫提供橋梁。
2)SQL API層:該層提供訪問數(shù)據(jù)庫的接口。用?QSqlDatabase類連接數(shù)據(jù)庫。用QSqlQuery類和數(shù)據(jù)庫進行交互。除了上述2個類,還有?QSqlError,?QSqlField,?QSqlIndex, 和?QSqlRecord.。
3)用戶接口層:該層的類把數(shù)據(jù)庫里的數(shù)據(jù)連接到widget,包括?QSqlQueryModel,?QSqlTableModel, 和?QSqlRelationalTableModel。
二、Connecting to Databases:連接到數(shù)據(jù)庫
訪問數(shù)據(jù)庫之前,首先要創(chuàng)建并打開一個或多個數(shù)據(jù)庫連接。數(shù)據(jù)庫連接通常用 [連接名] 來標(biāo)識。對同一個數(shù)據(jù)庫可以有多個連接。QSqlDatabase還支 [默認連接] 的概念,它是個未命名的連接。當(dāng)調(diào)用QSqlQuery或QSqlQuery的方法且其中需要[連接名]參數(shù)時,如果不傳遞一個[連接名],則使用[默認連接]。
當(dāng)應(yīng)用程序中只有一個連接時,使用默認連接是最方便的。
創(chuàng)建和打開連接是不同的。創(chuàng)建一個連接是創(chuàng)建一個QSqlDatabase對象,但是為打開前連接是不可用的。下面的代碼展示了如何創(chuàng)建一個連接并打開。

第一句是創(chuàng)建連接,最后一句是打開連接,中間是做一些準(zhǔn)備。參數(shù)“QMYSQL”用來指定數(shù)據(jù)庫驅(qū)動器的類型。Qt支持的數(shù)據(jù)庫類型在supported database drivers中。
上面的代碼展示的是【默認連接】,應(yīng)為沒有給?addDatabase()傳遞【連接名】。下面是兩個命名的連接:

使用?open() 函數(shù)打開連接。如果失敗了,則返回false,用?QSqlDatabase::lastError() 來獲取錯誤信息。
當(dāng)連接建立后,可以在任何地方使用靜態(tài)函數(shù)QSqlDatabase::database() 來獲取連接,如果不傳遞連接名,則返回默認連接。

想要移除連接,則先用?QSqlDatabase::close()關(guān)閉連接,然后用靜態(tài)函數(shù)QSqlDatabase::removeDatabase()移除。對于默認連接,用connectionName()獲得名字,然后移除。

三、Executing SQL Statements:執(zhí)行SQL語句
QSqlQuery?類提供了執(zhí)行SQL語句的接口以及獲取查詢結(jié)果集合的接口。
?QSqlQueryModel?and?QSqlTableModel?提供了訪問數(shù)據(jù)庫的更高層次的接口。如果你不熟悉SQL,可以直接跳轉(zhuǎn)到下一節(jié)。
要執(zhí)行SQL語句,簡單的創(chuàng)建一個QSqlQuery?對象并調(diào)用?QSqlQuery::exec()函數(shù)即可。

QSqlQuery?的構(gòu)造函數(shù)接收一個可選的?QSqlDatabase?對象來指定使用哪個連接。上面沒有傳遞該參數(shù),表明使用的是默認連接。
如果exec() 返回false,表明有錯誤,則調(diào)用?QSqlQuery::lastError()來查看。
如何查看查詢的結(jié)果呢?QSqlQuery提供了對結(jié)果集合的訪問,每次可以訪問一條記錄。調(diào)用exec() 后,QSqlQuery內(nèi)部的指針在first record的前面,因此必須調(diào)用一次?QSqlQuery::next()來獲取第一條記錄,然后繼續(xù)調(diào)用?QSqlQuery::next()來獲取其他記錄,直到返回false。
下面的代碼是獲取所有記錄的典型循環(huán):

?QSqlQuery::value() 返回當(dāng)前記錄的一個字段值。字段用zero-based的索引指定。?QSqlQuery::value()返回一個?QVariant, 該類型可以存儲很多種 C++ 和 Qt 數(shù)據(jù)類型,例如 int,?QString, and?QByteArray.?
對于Qt推薦的數(shù)據(jù)類型,查看此表this table.

可以用?QSqlQuery::next(),?QSqlQuery::previous(),?QSqlQuery::first(),?QSqlQuery::last(), 和?QSqlQuery::seek()來尋找各個記錄。?QSqlQuery::at()返回當(dāng)前記錄所在的行號。?QSqlQuery::size() 返回查詢結(jié)果集合中所有的記錄個數(shù)(行數(shù))。
?QSqlDriver::hasFeature()用來判斷數(shù)據(jù)庫驅(qū)動器是否支持特定的屬性。

如果你只是前向瀏覽,在exec() 前先調(diào)用?QSqlQuery::setForwardOnly(true)能顯著的加速對多結(jié)果查詢。
Insert、Update和Delete記錄

除了直接插入,qt還支持命名的占位符和索引的占位符,如下所示。


QSqlQuery::executedQuery()返回DBMS實際執(zhí)行的語句,可能跟?QSqlQuery里寫的不同(但作用相同)。
若果想要插入多條記錄,只需要調(diào)用?QSqlQuery::prepare() 一次,然后調(diào)用?bindValue() 或?addBindValue() 緊跟著?exec() 多次。
占位符的好處除了性能,還不用擔(dān)心轉(zhuǎn)義字符。
Update和Delete也可以用命名或位置的占位符。


事務(wù):Transaction
如果數(shù)據(jù)庫支持事務(wù),QSqlDriver::hasFeature(QSqlDriver::Transactions) 返回true. 用?QSqlDatabase::transaction() 來啟動事務(wù),然后執(zhí)行SQL語句,最后用?QSqlDatabase::commit() or?QSqlDatabase::rollback()來提交或回滾。當(dāng)使用transaction時,必須先啟動transaction然后再創(chuàng)建QSqlQuery,如下圖所示。

四、Using the SQL Model Classes:使用SQL model類
除了使用接近底層的?QSqlQuery,Qt還提供了更高層次的類來訪問數(shù)據(jù)庫,包括?QSqlQueryModel,?QSqlTableModel, 和QSqlRelationalTableModel.

QSqlQueryModel可以基于多表查詢,更靈活,而后兩者只能基于一張表,不太靈活。
這些類都派生自?QAbstractTableModel?(它又派生自?QAbstractItemModel),他們使得在View上(如QListView或QTableView)展示數(shù)據(jù)更方便。在下一節(jié)?Presenting Data in a Table View?中會討論。
使用這些類的另一個好處是修改數(shù)據(jù)源時代碼簡單,例如從數(shù)據(jù)庫改成xml數(shù)據(jù)。
SQL Query Model
QSqlQueryModel提供了一個基于SQL查詢的只讀model。

當(dāng)用setQuery()函數(shù)設(shè)置查詢后,可以用record(int)函數(shù)獲取單條記錄。還可以用data()函數(shù)獲取數(shù)據(jù),也可以用繼承自?QAbstractItemModel的函數(shù)。
setQuery()可以查詢多個table,view等內(nèi)容,很靈活。
?setQuery() 還有一個重載函數(shù),使用?QSqlQuery?對象來實現(xiàn)?QSqlQuery?的所有特性,如prepare函數(shù)。
如果數(shù)據(jù)庫不支持返回query size(SQLite就不支持),則rowCount()返回是的當(dāng)前緩存的行數(shù),而不一定是真實的查詢行數(shù),此時要看?canFetchMore()是否為真,然后用?fetchMore()獲取更多,直到?canFetchMore()為false。

SQL Table Model
QSqlTableModel提供了一個可讀可寫的模型,但每次只能工作在一張table上(沒有QSqlQueryModel靈活)。

QSqlTableModel?用來瀏覽和修改單個SQL table。
利用?QSqlTableModel::record(int)來獲取表格中的一行記錄,用?QSqlTableModel::setRecord() 來修改行。下面的例子是把工資提高10%。

修改數(shù)據(jù)后用submitAll()提交所有的待處理的改變。
還可以用?QSqlTableModel::data() 和?QSqlTableModel::setData()來訪問數(shù)據(jù),如下圖所示。

下圖展示了如何向QSqlTableModel中插入一行數(shù)據(jù)insertRows(),然后設(shè)置數(shù)據(jù)內(nèi)容setData(),最后提交數(shù)據(jù)庫submitAll()。

用removeRows()來刪除行,然后submitAll()提交給數(shù)據(jù)庫。

當(dāng)完成對記錄的改變,需要調(diào)用submitAll()來把修改的數(shù)據(jù)寫入到數(shù)據(jù)庫中。
何時調(diào)用submitAll()取決于QSqlTableModel的edit strategy,默認的策略是QSqlTableModel::OnRowChange,就是緩存一行的數(shù)據(jù),當(dāng)用戶選擇其他行時,更新數(shù)據(jù)庫。?QSqlTableModel::OnManualSubmit?是所有的改變都先緩存在model中,當(dāng)調(diào)用submitAll()時才寫數(shù)據(jù)庫;QSqlTableModel::OnFieldChange?是不緩存,直接寫數(shù)據(jù)庫。

SQL Relational Tabel Model
QSqlRelationalTableModel是對QSqlTableModel的擴展,它支持外鍵(foreign key),外鍵是另一個表的主鍵。
如下圖所示,左邊表格的(city, country)都是外鍵,且是數(shù)字,右邊表格則把外鍵修改成了用戶可讀的內(nèi)容。

下圖展示了?QSqlRelationalTableModel?如何設(shè)置的實例:
setRelation()函數(shù)用來設(shè)置外鍵,會用到?QSqlRelation對象。

五、Presenting Data in a Table View:在Table View中展示數(shù)據(jù)
QSqlQueryModel,QSqlTableModel和QSqlRenationalTableModel類可以用來作為Qt View的數(shù)據(jù)源,如QListView,?QTableView, 和QTreeView,其實最常用的還是?QTableView,因為SQL結(jié)果就是table。
下面是創(chuàng)建一個基于SQL data model的視圖,使用setModel()函數(shù):

如果model是read-write的模型(如QSqlTableModel),view允許用戶編輯字段。當(dāng)然也可以通過如下代碼禁用:

視圖會顯示表頭,想要改變表頭的文字,調(diào)用model的setHeaderData() ,默認情況下表頭是數(shù)據(jù)庫中table的字段頭。

QTableView?還有垂直的表頭來時識別行號。如果調(diào)用?QSqlTableModel::insertRows(),新的行會顯示*,直到調(diào)用?submitAll() 或用戶自動提交更改(?QSqlTableModel::OnRowChange)。

同樣,當(dāng)調(diào)用?removeRows()時,行前面會有!。
視圖中的item用代理來渲染,默認的代理是QStyledItemDelegate,它可以處理絕大多數(shù)的數(shù)據(jù)類型(如int,QString,QImage等)。當(dāng)用戶編輯view中的item時,代理同時負責(zé)提供編輯器widget(如combobox)。你也可以創(chuàng)建自己的代理,子類化?QAbstractItemDelegate?or?QStyledItemDelegate即可。
QSqlTableModel只能操作一個table。如果你需要一個能夠讀寫且能夠處理任意查詢結(jié)果的model,你可以子類化QSqlQueryModel并實現(xiàn)其中的flags() 和?setData() 函數(shù)來使他能夠read-write. 下面兩個函數(shù)能夠讓model查詢結(jié)果的字段1和字段2可編輯。

其中setFirstName()函數(shù)定義如下:

查看?Query Model?示例可以得到完整的代碼。
子類化一個model可以實現(xiàn)很多自定義的功能:例如給每個item提供tooltips,改變背景顏色、提供計算值等等。
如果需要解決外鍵的顯示,則可以用QSqlRelationalTableModel。推薦使用QSqlRelationalTableModel,有代理可以提供combobox對外鍵的編輯。
Relational Table Model示例展示了如何使用?QSqlRelationalTableModel?和?QSqlRelationalDelegate?來提供外鍵的支持。
六、Creating Data-Aware Forms:創(chuàng)建數(shù)據(jù)感知的Forms
有些時候,除了把model里的數(shù)據(jù)放到view里,還可能會放到其他widge里,比如SpinBox,comboBox,LineEditor等。
用QDataWidgetMapper可以把model中的數(shù)據(jù)映射到widget中。
QDataWidgetMapper處理一個指定一個數(shù)據(jù)庫table,通過行或列的形式映射數(shù)據(jù)。因此,使用QDataWidgetMapper和SQL model就像使用其他table model一樣簡單。

The?Books展示了如何使用QDataWidgetMapper以及一系列輸入widgets的案例。
SQLite QSqlQuery的size()方法始終返回-1
https://deepinout.com/sqlite/sqlite-questions/184_sqlite_qsqlquery_size_always_returns_1.html