我曾经想过,无论在哪个平台下开发,都不要再接触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"