我曾經想過,無論在哪個平台下開發,都不要再接觸SQL Server了,但顯然不行。我們是來看世界的,不是來改變世界的,想通就好。
前兩天,嘗試了一下Qt下遠程訪問數據庫。在macOS下,用Qt 5.11寫個程序來遠程訪問Win10下的SQL Server和My SQL數據庫,Qt中通過QSqlDatabase來創建一個數據庫連接。簡單來說,QSqlDatabase連接數據庫可以分為兩種方式,聊到這兩種方式,就要大概的說一下數據訪問的前因后果,以微軟的數據訪問歷史為例,本文只是從快速使用的角度出發,不會講得太詳細深入。
一、 數據訪問方式的歷史
在早期時,由於數據庫種類繁多,各種數據庫連接有不同的需求,數據庫連接主要依靠各種API函數來進行連接,數據庫編程都是直接操作數據庫廠商提供的API,每個數據庫廠商的提供的數據庫操作的API都不相同,如調用函數,操作語句等等。因此每個應用程序都只能對應一個數據庫。如果想換數據庫,需要重寫一遍數據庫操作代碼,這樣的代價是非常大的。因此Microsoft就聯合一些廠家做了個接口標准——ODBC。
ODBC(Open Database Connectivity,開放數據庫互連)
1992年Microsoft和Sybase、Digital共同制定了ODBC 接口標准,以標准的ODBC API來存取各種不同的數據庫。ODBC將所有數據庫特定的,底層的操作細節(CLI)封裝在驅動(drive)中,並提供一套標准的函數調用。使用時,ODBC會動態地加載數據庫的CLI,將函數調用轉換成各個數據庫的CLI調用。這樣應用程序與數據庫API本身就隔絕開了,ODBC本身也提供了對SQL語言的支持,用戶可以直接將SQL語句送給ODBC,如下圖所示。
這樣訪問所有的關系型數據庫都可以使用一套標准的ODBC API即可。ODBC成為最早的通用數據庫訪問技術。隨后ODBC便獲得了許多數據庫廠商和Third-Party的支持而逐漸成為標准的數據存取技術。
ODBC以當時的業界標准規范X/OpenCall-LevelInterface(CLI)和ISO/IEC9075-3Call-LevelInterface(SQL/CLI)為涵蓋的范圍,因而支持了廣闊的數據庫。雖然ODBC在初期的版本中執行效率不佳,而且功能有限,因此也為人們所貶低。但是,隨着Microsoft不斷地改善ODBC,使ODBC的執行效率不斷增加,ODBC驅動程序的功能也日漸齊全。到目前,ODBC已經是一個穩定並且執行效率良好的數據存取引擎。
OLE DB(Object Linking and Embedding DataBase,對象鏈接和嵌入數據庫)
ODBC是最早的通用數據訪問技術,但是ODBC只限於檢索關系型數據庫的數據。
1997年, Microsoft 的一個戰略性系統級編程接口,用於管理整個組織內的數據。OLE DB 是建立在 ODBC 功能之上的一個開放規范。ODBC 是為訪問關系型數據庫而專門開發的,OLE DB 則用於訪問關系型和非關系型信息源,例如主機 ISAM/VSAM 和層次數據庫,電子郵件和文件系統存儲,文本、圖形和地理數據以及自定義業務對象。
在OLE DB中,不再有drive的概念,取而代之的是提供者(provider),每個數據庫廠商都需要對象的OLE DB provider。需要注意的是,provider實現了基於COM的接口,這些接口封裝了訪問數據庫的操作細節(CLI)。那么應用程序使用這些通用的接口來進行數據庫的訪問,而不用考慮數據庫的細節。所以,可以理解OLE DB是規定了數據使用者和提供者之間達成了一種協議。
為了兼容一些沒有提供provider的數據庫,OLE DB也可以基於ODBC,即provider是基於ODBC實現的。這種實現會經過兩層,效率會比較低。由於目前大多數數據庫都提供了provider,所以這種方式比較少見。 可以看到,OLE DB與ODBC類似,但是原理上是不相同的。如下圖:
ADO(ActiveX Data Object,ActiveX數據對象)
微軟為了簡化OLE DB接口,推出了ADO來封裝OLE DB的接口,實現與數據庫的通信,使得用戶更易於調用數據庫相關操作。ADO實際上是位於OLE DB頂部的一個附加層(也就是位於OLE DB與應用程序之間),它封裝了OLE DB。
ADO被設計來繼承微軟早期的數據訪問對象層,包括RDO(Remote DataObjects)和DAO(Data Access Objects)。隨着.NET推出,微軟進一步進行升級ADO為ADO.NET。
二、Qt選程訪問數據
本開發環境在macOS平台下,用Qt 5.11開發程序來遠程訪問Win10下的數據庫。使用Parallesl安裝一個Win10 虛擬機,IP地址為10.211.55.4,在Win10中安裝了Visual Studio Community 2017、SQL Server 2008 Express、MySQL Community 8.0.14。
在Qt中,QSqlDatabase類提供一個通過數據庫連接來訪問數據庫的接口。一個QSqlDatabase的實例代表了一個數據庫連接。數據庫連接通過數據庫驅動提供對數據庫的訪問,數據庫驅動繼承自QSqlDriver。
QSqlDatabase 當前支持的驅動類型如下:
基於ODBC連接數據庫
准備工作1:安裝odbc manager, 不裝第二步會報錯
下載地址: http://www.odbcmanager.net/
准備工作2:安裝mysql odbc connector
下載地址: https://dev.mysql.com/downloads/connector/odbc/
開始編碼。
首先,在Qt的工程文件中加入:
QT += sql
在源代碼文件中引用以下頭文件,其中QPluginLoader、QDebug是為了調試用的:
#include <QSqlDatabase> #include <QSqlError> #include <QPluginLoader> #include <QDebug>
先寫個loadPlugin函數來檢測Qt 有沒編譯好的ODBC驅動,在 /Users/Hula/Qt/5.11.2/clang_64/plugins/sqldrivers/libqsqlodbc.dylib 對應填上你自己的Qt安裝文件路徑,代碼如下:
void HDbm::loadPlugin() { QPluginLoader loader; // ODBC 驅動插件的路徑 loader.setFileName("/Users/Hula/Qt/5.11.2/clang_64/plugins/sqldrivers/libqsqlodbc.dylib"); qDebug() << loader.load(); qDebug() << loader.errorString(); }
簡單的寫個數據庫連接函數:
int HDbm::createSQLServerConnection() { loadPlugin(); QString strHost = "10.211.55.4"; int port = 3306; QString strDbName = "SQLData"; QString strUserName = "test"; QString strUserPwd = "123321"; QSqlDatabase db = QSqlDatabase::addDatabase("QODBC"); QString strconn = QString("Driver={sql server};SERVER=%1;PORT=%2;DATABASE=%3;UID=%4;PWD=%5;") .arg(strHost) .arg(port) .arg(strDbName) .arg(strUserName) .arg(strUserPwd); db.setDatabaseName(strconn); if (!db.open()) { qDebug() <<"error_SqlServer:" << db.lastError().text(); return 1201; } return 0; }
調用createSQLServerConnection函數即可。
如果出現以下錯誤:
The shared library was not found. QSqlDatabase: QODBC driver not loaded ((null):0, (null))
表示Qt沒有 libqsqlodbc 驅動,需要自己動手編譯Qt的源碼,具體方法自動上網搜索。
使用Qt的QSqlDriver 數據庫驅動連接數據庫
首先安裝MySQL Connector/C,MySQL官網上沒有此安裝文件,有兩種方法來完成,一是用 brew 安裝 MySQL,brew 將 MySQL 安裝在 usr/local/lib/mysql 目錄下,由於Qt 調用 usr/local/lib 下的 MySQL 驅動,因此將 usr/local/lib/mysql 目錄下的驅動文件 libmysqlclient.dylib 鏈接到 usr/local/lib 目錄下即可。
或從 https://dev.mysql.com/downloads/mysql/ 下載MySQL Community Server的壓縮包,解壓后,拷貝 lib 目錄中文件到 usr/local/lib 下即可。
同上,MySQL數據庫連接函數:
int HDbm::createMySQLConnection() { loadPlugin(); QString strHost = "10.211.55.4"; int port = 3306; QString strDbName = "SQLData"; QString strUserName = "test"; QString strUserPwd = "123321"; QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL"); // 防止連接超時無反應,設置連接超時3秒 db.setConnectOptions("MYSQL_OPT_CONNECT_TIMEOUT=3"); db.setHostName(strHost); db.setPort(port); db.setDatabaseName(strDbName); db.setUserName(strUserName); db.setPassword(strUserPwd); if (!db.open()) { qDebug() <<"error_SqlServer:" << db.lastError().text(); return 1201; } return 0; }
三、補充說明
在Qt的文檔中對QSqlDatabase這個類的描述如下:
The QSqlDatabase class handles a connection to a database.
The QSqlDatabase class provides an interface for accessing a database through a connection. An instance of QSqlDatabase represents the connection. The connection provides access to the database via one of the supported database drivers, which are derived from QSqlDriver. Alternatively, you can subclass your own database driver from QSqlDriver. See How to Write Your Own Database Driver for more information.
如果你使用的數據庫不在上表Qt當前支持的驅動類型中,你可以從QSqlDriver類構建你自己的數據庫驅動。有兩種方法,一是找到數據庫廠商的 for ODBC dirver,如果沒有,也可以通過數據庫廠商的 for C/C++ dirver 自己封裝一個 for ODBC dirver,通過 ODBC 訪問數據庫;再就是從QSqlDriver類構建一個的數據庫驅動,來調用數據庫廠商的 for C dirver,更為方便簡單。
另,ODBC不同數據庫連接字符串:
access "Driver={microsoft access driver(*.mdb)};dbq=*.mdb;uid=admin;pwd=pass;" dBase "Driver={microsoft dbase driver(*.dbf)};driverid=277;dbq=***;" oracle "Driver={microsoft odbc for oracle};server=oraclesever.world;uid=admin;pwd=pass;" MSSQL server "Driver={sql server};server=servername;database=dbname;uid=sa;pwd=pass;" MS text "Driver={microsoft text driver(*.txt; *.csv)};dbq=**;extensions=asc,csv,tab,txt;Persist SecurityInfo=false;" Visual Foxpro "Driver={microsoft Visual Foxpro driver};sourcetype=DBC;sourceDB=*.dbc;Exclusive=No;" MySQL "Driver={mysql};database=yourdatabase;uid=username;pwd=yourpassword;option=16386;" SQLite "Driver={SQLite3 ODBC Driver};Database=D:\SQLite\*.db" PostgreSQL "Driver={PostgreSQL ANSI};server=127.0.0.1;uid=admin;pwd=pass;database=databaseName"