關(guān)于Qt數(shù)據(jù)庫(kù)相關(guān)開(kāi)發(fā)的一些經(jīng)驗(yàn)總結(jié)
一、前言
近期花了兩個(gè)多月時(shí)間,將數(shù)據(jù)庫(kù)相關(guān)的代碼重新封裝成了各種輪子(這條路必須打通,打通以后,相關(guān)項(xiàng)目只需要引入這個(gè)組件pri即可),測(cè)試了從Qt4.7到Qt6.1的各種版本,測(cè)試了odbc、sqlite、mysql、postgresql、sqlserver、oracle、人大金倉(cāng)等數(shù)據(jù)庫(kù),測(cè)試了本地連接、遠(yuǎn)程連接、阿里云連接等,測(cè)試了windows、linux、mac等系統(tǒng),將所有項(xiàng)目數(shù)據(jù)庫(kù)相關(guān)的代碼全部更新了一遍。能夠兼容這么多Qt版本和數(shù)據(jù)庫(kù)插件以及測(cè)試驗(yàn)證系統(tǒng),估計(jì)全網(wǎng)也沒(méi)幾個(gè)人,全國(guó)11W Qter開(kāi)發(fā)者中應(yīng)該也是最多不超過(guò)10人。二、數(shù)據(jù)庫(kù)開(kāi)發(fā)經(jīng)驗(yàn)總結(jié)
- 在數(shù)據(jù)庫(kù)相關(guān)的應(yīng)用中,如果僅僅是單機(jī)版本,沒(méi)有特別的需要(比如領(lǐng)導(dǎo)指定,或者需要遠(yuǎn)程存放數(shù)據(jù)),強(qiáng)烈建議使用sqlite數(shù)據(jù)庫(kù),這是本人經(jīng)過(guò)無(wú)數(shù)次的對(duì)比測(cè)試和N個(gè)商業(yè)項(xiàng)目應(yīng)用得出的結(jié)論。
- Qt天生內(nèi)置了sqlite數(shù)據(jù)庫(kù),只需要發(fā)布的時(shí)候帶上插件就行(可以看到插件動(dòng)態(tài)庫(kù)文件比其他幾種都要大,那是因?yàn)橹苯訉?shù)據(jù)庫(kù)的源碼都編譯進(jìn)去了,而其他只編譯了中間通信交互的插件源碼),其他數(shù)據(jù)庫(kù)要么還要帶上動(dòng)態(tài)庫(kù),要么還需要?jiǎng)?chuàng)建數(shù)據(jù)源;
- 速度上,絕對(duì)無(wú)與倫比的出類(lèi)拔萃,同樣的數(shù)據(jù)庫(kù)結(jié)構(gòu)(表結(jié)構(gòu)、索引等完全一致),查詢(xún)速度和批量更新速度、數(shù)據(jù)庫(kù)事務(wù)等,速度都是其他幾種的至少3倍以上,而且隨著數(shù)據(jù)量的增大對(duì)比越發(fā)明顯;
- 幾千萬(wàn)的數(shù)據(jù)量完全沒(méi)問(wèn)題,而且速度和性能都還可以,不要以訛傳訛網(wǎng)上部分菜雞說(shuō)的不支持百萬(wàn)以上的數(shù)據(jù)量,本人親測(cè)億級(jí)別,數(shù)據(jù)量建議千萬(wàn)級(jí)別以下,著重注意數(shù)據(jù)庫(kù)表和索引的設(shè)計(jì);
- 其他數(shù)據(jù)庫(kù)還要注意版本的區(qū)別,ODBC數(shù)據(jù)源形式還容易出錯(cuò)和執(zhí)行失?。?/span>
- sqlite數(shù)據(jù)庫(kù)也有幾個(gè)重大缺點(diǎn):不支持加密,不支持網(wǎng)絡(luò)訪問(wèn),不支持部分?jǐn)?shù)據(jù)庫(kù)高級(jí)特性,不支持海量數(shù)據(jù)(億級(jí)別以上),但是對(duì)于絕大部分Qt項(xiàng)目還是足夠;
- 數(shù)據(jù)庫(kù)支持友好度大致是 sqlite > postgresql > mysql > odbc ;
- 以上都是在Qt環(huán)境中個(gè)人測(cè)試得出的結(jié)論,結(jié)果未必正確,作為參考即可,其他編程環(huán)境比如C#、JAVA請(qǐng)忽略,也許差別可能在中間通信的效率造成的;
- Qt5默認(rèn)提供的數(shù)據(jù)庫(kù)插件包括了QSQLITE、QMYSQL、QPSQL、QODBC四種,后期版本比如5.12開(kāi)始把mysql也移除了(可能是因?yàn)殚_(kāi)源協(xié)議的問(wèn)題),其中驅(qū)動(dòng)打印中還有個(gè)QMYSQL3是表示mysql3舊版本,現(xiàn)在默認(rèn)一般都mysql5以上,QPSQL7表示postgres7舊版本,現(xiàn)在默認(rèn)一般都postgres9以上。
- 根據(jù)字面意思很容易理解QSQLITE用來(lái)連接sqlite數(shù)據(jù)庫(kù),QMYSQL用來(lái)連接mysql數(shù)據(jù)庫(kù),QPSQL用來(lái)連接postgres數(shù)據(jù)庫(kù),QODBC理論上可以用來(lái)連接任何支持ODBC數(shù)據(jù)源的數(shù)據(jù)庫(kù),比如access、sqlserver、mysql、postgres、oracle等。
- Qt4默認(rèn)提供的數(shù)據(jù)庫(kù)插件只有QSQLITE、QODBC兩種,因?yàn)镼ODBC理論上可以用來(lái)連接任何支持ODBC數(shù)據(jù)源的數(shù)據(jù)庫(kù),只是通過(guò)了微軟的數(shù)據(jù)源中間件,效率上可能會(huì)有損耗,所以在Qt5又新增了其他幾個(gè)常用數(shù)據(jù)庫(kù)的插件比如QMYSQL、QPSQL,而其他數(shù)據(jù)庫(kù)由于協(xié)議的要求并沒(méi)有提供對(duì)應(yīng)的插件需要自行編譯比如oracle。
- Qt內(nèi)置了sqlite數(shù)據(jù)庫(kù),可以觀察到qsqlite4.dll文件大小明顯比其他數(shù)據(jù)庫(kù)插件大很多,理論上光一個(gè)插件應(yīng)該小很多才對(duì),畢竟sqlite屬于小型數(shù)據(jù)庫(kù),所以肯定是將sqlite的源碼直接編譯到插件了,所以我們?cè)谑褂胹qlite數(shù)據(jù)庫(kù)的時(shí)候無(wú)需帶一個(gè)sqlite.dll,而使用mysql則需要帶上libmysql.dll。
- 使用mysql、postgres等支持遠(yuǎn)程訪問(wèn)的數(shù)據(jù)庫(kù)的時(shí)候,并不需要本地安裝數(shù)據(jù)庫(kù),只需要發(fā)布程序的時(shí)候帶上對(duì)應(yīng)數(shù)據(jù)庫(kù)的動(dòng)態(tài)庫(kù)即可,比如mysql對(duì)應(yīng)帶上libmysql.dll即可,這樣程序指定數(shù)據(jù)庫(kù)主機(jī)地址就可以連接上,比如阿里云的mysql、postgres等云端數(shù)據(jù)庫(kù)。
- mysql、posgrest等支持遠(yuǎn)程連接的數(shù)據(jù)庫(kù),默認(rèn)安裝以后出于安全性考慮只支持本地訪問(wèn),需要做設(shè)置才能支持遠(yuǎn)程訪問(wèn),mysql需要增加用戶(hù)root@%即主機(jī)設(shè)置為%,postgres需要打開(kāi)安裝目錄下的C:\PostgreSQL\10\data\pg_hba.conf文件,增加一行 host all all 192.168.1.0/24 md5 表示支持192.168.1.1到192.168.1.255的IP訪問(wèn),同時(shí)將C:\PostgreSQL\10\data\postgresql.conf改成listen_addresses = '*'表示支持所有地址,具體這個(gè)的含義可以自行搜索。
- mysql數(shù)據(jù)庫(kù)通信的默認(rèn)端口是3306,postgres的是5432,這些端口都可以在安裝的時(shí)候或者后期更改。
- 數(shù)據(jù)庫(kù)也有位數(shù)的區(qū)別,比如你連接的是64位的數(shù)據(jù)庫(kù)那就需要用64位的Qt以及64位的數(shù)據(jù)庫(kù)插件和對(duì)應(yīng)的動(dòng)態(tài)庫(kù)文件,位數(shù)一定要完全一致才行,否則連不上,很多人會(huì)在這個(gè)地方摔一跤。除了位數(shù)的區(qū)別可能還要注意版本的區(qū)別,畢竟數(shù)據(jù)庫(kù)一直在更新升級(jí)換代,有些版本變動(dòng)比較大,未必Qt發(fā)布版本的時(shí)候?qū)?yīng)就支持最新的數(shù)據(jù)庫(kù),所以一般建議用稍微老一點(diǎn)的數(shù)據(jù)庫(kù)版本,比如mysql本人一直用5.6,測(cè)試到現(xiàn)在Qt5.13版本都支持。
- 一般的軟件默認(rèn)都只需要連接一個(gè)數(shù)據(jù)庫(kù),所以建議直接在程序啟動(dòng)以后就打開(kāi)好數(shù)據(jù)庫(kù),然后其他需要用到數(shù)據(jù)庫(kù)的地方就執(zhí)行即可,最后程序關(guān)閉的時(shí)候關(guān)閉數(shù)據(jù)庫(kù)。很多初學(xué)者每次增刪改查都打開(kāi)數(shù)據(jù)庫(kù)執(zhí)行完成操作以后然后關(guān)閉數(shù)據(jù)庫(kù),這樣效率極其低下。如果需要連接多個(gè)數(shù)據(jù)庫(kù),則以數(shù)據(jù)庫(kù)連接名稱(chēng)作為區(qū)分,Qt支持同時(shí)多個(gè)數(shù)據(jù)庫(kù)連接的,數(shù)據(jù)庫(kù)跨線程不安全,要加鎖,所以建議在哪個(gè)線程使用到的數(shù)據(jù)庫(kù)就在那個(gè)線程中打開(kāi),而不要主線程打開(kāi)數(shù)據(jù)庫(kù)子線程使用數(shù)據(jù)庫(kù),很可能會(huì)出問(wèn)題。Qt5.10開(kāi)始增加了數(shù)據(jù)庫(kù)跨線程使用的安全性檢查,運(yùn)行時(shí)會(huì)打印提示。
- 創(chuàng)建數(shù)據(jù)庫(kù)、創(chuàng)建表、創(chuàng)建索引、初始化數(shù)據(jù)等這些都可以通過(guò)執(zhí)行sql語(yǔ)句來(lái)實(shí)現(xiàn),強(qiáng)烈建議在對(duì)常用的數(shù)據(jù)量比較多的表創(chuàng)建表的時(shí)候要?jiǎng)?chuàng)建索引,在大量的數(shù)據(jù)查詢(xún)更新操作的時(shí)候先啟動(dòng)數(shù)據(jù)庫(kù)事務(wù),執(zhí)行完成以后提交數(shù)據(jù)庫(kù)事務(wù)。
void MainWindow::testDb()
{
//打印當(dāng)前Qt對(duì)應(yīng)支持的數(shù)據(jù)庫(kù)驅(qū)動(dòng)名稱(chēng)
qDebug() << QSqlDatabase::drivers();
//創(chuàng)建數(shù)據(jù)庫(kù)對(duì)象,驅(qū)動(dòng)名稱(chēng)根據(jù)打印的填寫(xiě),"QSQLITE", "QMYSQL", "QMYSQL3", "QODBC", "QODBC3", "QPSQL", "QPSQL7"
QSqlDatabase db = QSqlDatabase::addDatabase("QPSQL");
//設(shè)置數(shù)據(jù)庫(kù)參數(shù),要查看Qt文檔是否支持該數(shù)據(jù)庫(kù),一般建議默認(rèn)的就好不用設(shè)置
//db.setConnectOptions("MYSQL_OPT_RECONNECT=1;MYSQL_OPT_CONNECT_TIMEOUT=1;");
//設(shè)置數(shù)據(jù)庫(kù)的主機(jī)地址
db.setHostName("127.0.0.1");
//設(shè)置數(shù)據(jù)庫(kù)通信端口,默認(rèn)值 mysql:3306 postgres:5432 sqlserver:1433
db.setPort(5433);
//設(shè)置數(shù)據(jù)庫(kù)名稱(chēng),默認(rèn)值 mysql:mysql postgres:postgres sqlserver:master
//如果是sqlite數(shù)據(jù)庫(kù)只需要設(shè)置這個(gè)參數(shù)即可,其余參數(shù)都不用設(shè)置,因?yàn)閟qlite不需要主機(jī)端口和用戶(hù)
//參數(shù)內(nèi)容為數(shù)據(jù)庫(kù)文件的路徑 db.setDatabaseName("c:/test.db");
db.setDatabaseName("postgres");
//設(shè)置登錄用戶(hù)名稱(chēng),默認(rèn)值 mysql:root postgres:postgres sqlserver:sa
db.setUserName("postgres");
//設(shè)置登錄用戶(hù)密碼
db.setPassword("admin");
//打開(kāi)數(shù)據(jù)庫(kù),如果失敗打印錯(cuò)誤信息
if (!db.open()) {
qDebug() << db.lastError();
return;
}
//執(zhí)行增刪改查
//常規(guī)查詢(xún)語(yǔ)句
QString sql = "select UserName,UserPwd from UserInfo";
//帶條件 排序 分組的查詢(xún)語(yǔ)句
sql = "select UserName,UserPwd from UserInfo where UserName='admin' order by UserName asc group by UserGroup";
//構(gòu)建查詢(xún)對(duì)象,傳入sql語(yǔ)句查詢(xún),可以先判斷執(zhí)行成功與否再來(lái)取值
QSqlQuery query;
if (query.exec(sql)) {
//循環(huán)取出所有查詢(xún)結(jié)果,對(duì)應(yīng)結(jié)果是QVariant類(lèi)型可以自行to到其他類(lèi)型
while(query.next()) {
qDebug() << query.value(0).toString() << query.value(1).toString();
}
}
//添加數(shù)據(jù),拼接字符串的形式比較通用,還有占位符的形式
sql = "insert into UserInfo(UserName,UserPwd) values('ceshi', '12345')";
//刪除數(shù)據(jù),如果不加where條件則表示刪除整個(gè)表的數(shù)據(jù)
sql = "delete from UserInfo where UserName='ceshi'";
//更新數(shù)據(jù),如果不加where條件則表示更新整個(gè)表的數(shù)據(jù)
sql = "update UserInfo set UserPwd='admin123' where UserName='ceshi'";
//可以復(fù)用上面的QSqlQuery對(duì)象,也可以重新new,復(fù)用的話需要先調(diào)用clear
query.clear();
//添加 刪除 更新 數(shù)據(jù)只需要知道執(zhí)行成功與否就行
if (!query.exec(sql)) {
qDebug() << "執(zhí)行sql語(yǔ)句失敗";
}
//關(guān)閉數(shù)據(jù)庫(kù),程序自動(dòng)關(guān)閉的時(shí)候也會(huì)關(guān)閉,所以只是用一個(gè)數(shù)據(jù)庫(kù)的情況下無(wú)需手動(dòng)關(guān)閉
db.close();
}
五、數(shù)據(jù)庫(kù)綜合應(yīng)用組件(一)功能特點(diǎn)- 同時(shí)支持多種數(shù)據(jù)庫(kù)比如odbc、sqlite、mysql、postgresql、sqlserver、oracle、人大金倉(cāng)等。
- 一個(gè)數(shù)據(jù)庫(kù)類(lèi)即可管理本地?cái)?shù)據(jù)庫(kù)通信,也支持遠(yuǎn)程數(shù)據(jù)庫(kù)通信等。
- 數(shù)據(jù)庫(kù)線程支持執(zhí)行各種sql語(yǔ)句,包括單條和批量。
- 組件中的所有類(lèi)打印信息、錯(cuò)誤信息、執(zhí)行結(jié)果都信號(hào)發(fā)出去。
- 集成數(shù)據(jù)庫(kù)通用翻頁(yè)類(lèi)(負(fù)責(zé)具體處理邏輯),搭配分頁(yè)導(dǎo)航控件(負(fù)責(zé)外觀),形成超級(jí)牛逼的翻頁(yè)控件。
- 集成數(shù)據(jù)庫(kù)自動(dòng)清理類(lèi),設(shè)定最大記錄數(shù)后臺(tái)自動(dòng)清理早期數(shù)據(jù)。
- 集成自定義委托類(lèi),支持復(fù)選框、文本框、下拉框、日期框、微調(diào)框、進(jìn)度條等。
- 同時(shí)支持Qt4-Qt6,親測(cè)Qt4.6到Qt6.1任意版本,任意系統(tǒng)和編譯器。
- 本組件無(wú)故障 360天7乘24小時(shí) 運(yùn)行在至少上萬(wàn)個(gè)現(xiàn)場(chǎng),商業(yè)級(jí)別品質(zhì)保證。
- 每個(gè)類(lèi)都對(duì)應(yīng)完整詳細(xì)的使用示例,注釋詳細(xì),非常適合閱讀學(xué)習(xí)。
- 可以作為獨(dú)立的程序運(yùn)行,比如自動(dòng)清理早期數(shù)據(jù),同步數(shù)據(jù)到云端。
- 全部線程處理,不卡界面,自動(dòng)重連數(shù)據(jù)庫(kù)。
- 普通測(cè)試情況,sqlite數(shù)據(jù)庫(kù),數(shù)據(jù)庫(kù)發(fā)生器每秒鐘插入1000條記錄約0.003秒鐘,同時(shí)自動(dòng)清理數(shù)據(jù)類(lèi)每秒鐘刪除1000條記錄約0.13秒,不同線程互不干擾。
- 可設(shè)置數(shù)據(jù)庫(kù)類(lèi)型,支持多種數(shù)據(jù)庫(kù)類(lèi)型。
- 數(shù)據(jù)庫(kù)類(lèi)型包括但不限于odbc、sqlite、mysql、postgresql、sqlserver、oracle、人大金倉(cāng)等。
- 可設(shè)置數(shù)據(jù)庫(kù)連接信息包括主機(jī)地址、用戶(hù)信息等。
- 具有自動(dòng)重連機(jī)制,可設(shè)置是否檢查連接以及檢查間隔。
- 支持單條sql語(yǔ)句隊(duì)列,一般用于查詢(xún)返回?cái)?shù)據(jù),每次插入一條執(zhí)行一條。
- 支持多條sql語(yǔ)句隊(duì)列,一般用于遠(yuǎn)程提交數(shù)據(jù),每次插入一條執(zhí)行多條。
- 支持批量sql語(yǔ)句隊(duì)列,一般用于批量更新數(shù)據(jù),每次插入多條執(zhí)行多條。
- 可設(shè)置隊(duì)列最大數(shù)量,限定排隊(duì)處理的sql語(yǔ)句集合。
- 通過(guò)信號(hào)發(fā)出 打印信息、錯(cuò)誤信息、查詢(xún)結(jié)果。
- 可設(shè)置每頁(yè)多少行記錄,自動(dòng)按照設(shè)定的值進(jìn)行分頁(yè)。
- 可設(shè)置要查詢(xún)的表名、字段集合、條件語(yǔ)句、排序語(yǔ)句。
- 可設(shè)置第一頁(yè)、上一頁(yè)、下一頁(yè)、末一頁(yè)、翻頁(yè)按鈕。
- 可設(shè)置當(dāng)前頁(yè)、總頁(yè)數(shù)、總記錄數(shù)、每頁(yè)記錄數(shù)、查詢(xún)用時(shí)標(biāo)簽頁(yè)。
- 多線程查詢(xún)總記錄數(shù),數(shù)據(jù)量巨大時(shí)候不會(huì)卡主界面。
- 建議條件字段用整型類(lèi)型的主鍵,速度極快。
- 提供查詢(xún)結(jié)果返回信號(hào),包括當(dāng)前頁(yè)、總頁(yè)數(shù)、總記錄數(shù)、查詢(xún)用時(shí)等信息。
- 可設(shè)置所有列或者某一列對(duì)齊樣式例如居中或者右對(duì)齊。
- 可增加列用于標(biāo)識(shí)該條記錄,設(shè)定列的位置、標(biāo)題、寬度。
- 提供函數(shù)直接執(zhí)行第一頁(yè)、上一頁(yè)、下一頁(yè)、末一頁(yè)。
- 提供函數(shù)直接跳轉(zhuǎn)到指定頁(yè)。
- 根據(jù)是否第一頁(yè)、末一頁(yè)自動(dòng)禁用對(duì)應(yīng)的按鈕。
- 本控件是翻頁(yè)功能類(lèi),和翻頁(yè)控件navpage完美搭配,形成超級(jí)牛逼的翻頁(yè)控件。
- 可設(shè)置頁(yè)碼按鈕的個(gè)數(shù)。
- 可設(shè)置字體大小。
- 可設(shè)置邊框圓角角度、大小、顏色。
- 可設(shè)置正常狀態(tài)背景顏色、文字顏色。
- 可識(shí)別懸停狀態(tài)背景顏色、文字顏色。
- 可設(shè)置按下?tīng)顟B(tài)背景顏色、文字顏色。
- 可設(shè)置選中狀態(tài)背景顏色、文字顏色。
- 可設(shè)置導(dǎo)航位置居中對(duì)齊、左對(duì)齊、右對(duì)齊。
- 可設(shè)置是否顯示提示標(biāo)簽控件。
- 自動(dòng)計(jì)算總頁(yè)碼數(shù)顯示隱藏多余按鈕。
- 自動(dòng)計(jì)算切換頁(yè)碼導(dǎo)航。
- 和分頁(yè)導(dǎo)航功能類(lèi)無(wú)縫對(duì)接完美融合。
- 可設(shè)置要清理的對(duì)應(yīng)數(shù)據(jù)庫(kù)連接名稱(chēng)和表名。
- 可設(shè)置條件字段。
- 可設(shè)置排序字段。
- 可設(shè)置最大保留的記錄數(shù)。
- 可設(shè)置執(zhí)行自動(dòng)清理的間隔。
- 后期支持多個(gè)數(shù)據(jù)庫(kù)和多個(gè)表。
- 建議條件字段用數(shù)字類(lèi)型的主鍵,速度極快。
- 增加統(tǒng)計(jì)用字段名稱(chēng)設(shè)置。
- 增加自動(dòng)清理文件夾,超過(guò)大小自動(dòng)刪除文件夾中早期文件。
- 可設(shè)置多種委托類(lèi)型,例如復(fù)選框、文本框、下拉框、日期框、微調(diào)框、進(jìn)度條等。
- 可設(shè)置是否密文顯示,一般用于文本框。
- 可設(shè)置是否允許編輯,一般用于下拉框。
- 可設(shè)置是否禁用,一般用來(lái)禁用某列。
- 可設(shè)置數(shù)據(jù)集合,比如下拉框數(shù)據(jù)集合。
- 提供值變化信號(hào),比方說(shuō)下拉框值改動(dòng)觸發(fā)。
- 可設(shè)置數(shù)據(jù)校驗(yàn)自動(dòng)產(chǎn)生不同的圖標(biāo)。
- 支持設(shè)置校驗(yàn)列、校驗(yàn)規(guī)則、校驗(yàn)值、校驗(yàn)成功圖標(biāo)、校驗(yàn)失敗圖標(biāo)、圖標(biāo)大小。
- 可設(shè)置校驗(yàn)數(shù)據(jù)產(chǎn)生不同的背景顏色和文字顏色。
- 校驗(yàn)規(guī)則支持 == > >= < <= != contains,非常豐富。
- 復(fù)選框自動(dòng)居中而不是左側(cè),切換選中狀態(tài)發(fā)送對(duì)應(yīng)的信號(hào)。
- 可設(shè)置顏色委托,自動(dòng)根據(jù)顏色值繪制背景顏色,自動(dòng)設(shè)置最佳文本顏色。
- 可設(shè)置按鈕委托,自動(dòng)根據(jù)值生成多個(gè)按鈕,按鈕按下發(fā)送對(duì)應(yīng)的信號(hào)。
- 當(dāng)設(shè)置了委托列時(shí)自動(dòng)繪制選中背景色和文字顏色。
- 可設(shè)置關(guān)鍵字對(duì)照表繪制關(guān)鍵字比如原始數(shù)據(jù)是 0-禁用 1-啟用。
- 可設(shè)置復(fù)選框?qū)?yīng)的映射選中不選中關(guān)鍵字。
- 根據(jù)不同的委托類(lèi)型繪制,可以依葫蘆畫(huà)瓢自行增加自己的委托。
- 所有功能封裝成1個(gè)類(lèi),核心代碼不到500行,使用極其方便友好。