ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

QT连接Oracle数据库:QOCI驱动与32位依赖包完整部署指南

QT连接Oracle数据库:QOCI驱动与32位依赖包完整部署指南 简介面向在 32 位 Windows 环境下通过 QT5.13 连接 Oracle 11g 的开发者这是一份经预编译的驱动与依赖合集基于 MSVC 构建适用于 Visual Studio 编译的 QT 工程不兼容 MinGW。压缩包共 51 个文件包含 29 个头文件、10 个 dll 动态库、7 个 lib 静态库、4 个 sym 符号文件及 1 份说明文档整体约 60.63MB覆盖了 QT 驱动、OCI 客户端库和运行时依赖。已有 362 人学习/下载适合需要快速在 QT5.13 中集成 Oracle 11g 的开发者。资源内已备好编译好的 QOCI 驱动含调试版并集成了 Oracle 客户端的关键 OCI 动态库同时附带必要的头文件与静态链接库用户只需部署到对应目录并配置工程文件即可省去手动编译驱动的繁琐步骤直接开始数据库应用的开发与调试。1. 先说明白QT连Oracle为什么需要额外驱动包项目移交到我手上时对方丢过来的就是一个名字很长的压缩包QT5.13连接Oracle11的驱动和依赖32位.rar。当时程序里数据库连接一直报“Driver not loaded”代码翻了个遍也没看出毛病最后才明白问题根本不在代码而在驱动链。先解释背景。QT 5.13的数据库访问是插件架构默认带SQLite、ODBC、MySQL等驱动但偏偏没有Oracle的QOCI驱动。原因倒不复杂Oracle客户端的SDK体积大而且Instant Client的分发协议不允许QT官方捆绑所以官方文档明确写了QOCI需要使用者自己编译。光有插件还不够QOCI插件本身不跟Oracle服务器直接说话它走的是Oracle的OCIOracle Call Interface接口于是又依赖Oracle客户端那一堆DLL。整条链路是这样QT应用 → qsqloci.dllQT驱动插件 → oci.dll和一堆ora*.dllOracle客户端 → Oracle服务器这条链上任何一节断了表现都是连接失败。而“驱动和依赖”这个压缩包解决的就是这条链上除服务器外的所有环节。无论你是维护老旧工控项目、在厂内系统里接Oracle 11还是被安排去处理一台32位嵌入式上位机的数据库查询这套整理逻辑都通用。1.1 为什么偏偏32位环境最麻烦32位麻烦在哪第一位数必须严格一致。32位的QT进程只能加载32位DLLOracle客户端也必须用32位版本。很多人在Oracle官网下载时想都不想就装了64位Instant Client程序一起就被Windows怼回来找不到oci.dll或者直接提示“不是有效的Win32应用程序”。第二编译工具链必须一致。QT 5.13在Windows上有MinGW 7.3.0 32位、MSVC 2017 32位等不同构建。qsqloci.dll不是跨编译器通用的MinGW版插件放进MSVC版Qt里照样报Driver not loaded。压缩包里的插件是哪个编译器编译的你的QT就必须是同一套。我见过有人在MinGW环境里强行把MSVC版插件拷进去用结果卡了一整天。第三Oracle客户端依赖的C运行时版本很老。Oracle 11.2的Instant Client是基于VS2013编译的需要msvcp120.dll、msvcr120.dll。很多精简系统上恰恰没有这些运行库所以依赖包里必须给它们留位置。这台设备为什么要单独打一个32位依赖包原因就在这。2. QOCI驱动的依赖链和文件清单缺一个都白搭拆开rar之后里面的文件大致是下面这个结构不同来源的包可能有细微差别instantclient_32/ ├─ qsqloci.dll # QT的QOCI驱动插件 ├─ oci.dll # OCI API入口核心中的核心 ├─ oraociei11.dll # Oracle 11的OCI基础实现约250MB ├─ oraocci11.dll # C接口库部分功能需要 ├─ oraons.dll # Oracle连接管理 ├─ tnsnames.ora # 网络服务名配置 ├─ nls/ # 字符集数据目录 ├─ msvcp120.dll # VS2013运行库精简系统常缺 └─ msvcr120.dllqsqloci.dll是QT侧的翻译官负责把QSqlDatabase调用翻译成OCI调用oci.dll是Oracle客户端的总入口大量导出函数从它那里来oraociei11.dll才是真正干活的库字符集、网络协议处理都在里面oraocci11.dll是C封装层部分内部组件会用到oraons.dll管连接管理和负载均衡少它可能不会马上报错但连接多了之后会遇见奇怪超时。2.1 每个文件缺失的典型表现下面这个表是我实际排查中遇到过的现象不是凭空推测缺失文件程序表现qsqloci.dllQSqlDatabase::drivers()里没有QOCI打开时报“QOCI driver not loaded”oci.dllexe启动后直接报“找不到oci.dll”或编译时提示链接错误oraociei11.dllqsqloci能加载但数据库初始化失败或一查中文就乱码msvcp120.dll / msvcr120.dllexe启动时报“VCRUNTIME140.dll”之类的运行库错误tnsnames.ora用TNS别名连接时提示ORA-12154注意oraociei11.dll的体积。有人为了精简一看见250MB就把它砍掉结果Oracle驱动加载时缺字符集数据排查起来比多带一个文件痛苦得多。除非空间极端紧张我建议保留整个客户端子目录别动它。2.2 驱动和依赖包的版本匹配原则这个压缩包名字里写了QT5.13和Oracle11但并不表示只要这两个关键词对上复制粘贴就能成功。真正要匹配的是三条线QT版本与MinGW/MSVC构建是不是同一套驱动插件必须来自同一编译链Oracle客户端位数必须是32位且尽量选用Instant Client 11.2.0.x系列版本太新可能与Oracle 11服务端协商时出现字符集兼容问题Windows系统版本和VC运行库支持要够Win7 32位和Win10 32位在运行时上有细微差别发布时必须带上对应的VC运行库。把这三条当成一组验证条件拿到任何来源不明的qsqloci.dll时先确认再复制。3. 部署和配置实操解压到跑通那一步这一节我把从零到能连上Oracle的完整操作写出来。假设你已经把rar解压到本地目录例如C:\oracle\instantclient_32。3.1 插件放哪Oracle客户端放哪qsqloci.dll需要放到QT安装目录下的plugins/sqldrivers文件夹。以常见的MinGW 32位套件为例C:\Qt\5.13.0\mingw73_32\plugins\sqldrivers如果你用的是MSVC 2017 32位套件就放进对应的msvc2017_32目录不要放混。Oracle客户端这一堆DLL我建议单独放一个目录不要一股脑扔进system32或者exe同目录。原因是这些DLL不仅给当前程序用之后可能还会用QT写第二个、第三个工具路径集中管理最省心。3.2 环境变量的设置顺序右键“我的电脑” → 属性 → 高级系统设置 → 环境变量按顺序做三件事在系统变量PATH的最前面加上C:\oracle\instantclient_32。注意放最前面不要追加到末尾否则可能被同名的旧版DLL抢先加载。新增系统变量TNS_ADMIN指向存放tnsnames.ora的目录。如果tnsnames.ora就放在instantclient_32下TNS_ADMIN就写C:\oracle\instantclient_32。如果要处理中文新增系统变量NLS_LANG值设为SIMPLIFIED CHINESE_CHINA.ZHS16GBK。改完环境变量之后最容易被忽略的一步重启QT Creator。IDE在启动时读取环境变量不重启的话PATH里看不到新添加的路径代码里怎么调都白搭。直接用命令行运行程序也一样先重开一个cmd再跑。3.3 用一个最小工程验证驱动是否被识别新建一个QT Console工程在.pro里加上QT sqlmain.cpp改成这样#include QCoreApplication #include QSqlDatabase #include QSqlQuery #include QSqlError #include QDebug int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); qDebug() available drivers: QSqlDatabase::drivers(); QSqlDatabase db QSqlDatabase::addDatabase(QOCI); db.setHostName(192.168.1.100); db.setPort(1521); db.setDatabaseName(//192.168.1.100:1521/orcl); // EZCONNECT写法 db.setUserName(scott); db.setPassword(tiger); if (!db.open()) { qDebug() open failed: db.lastError().text(); return 1; } QSqlQuery query(db); query.exec(SELECT sysdate FROM dual); while (query.next()) { qDebug() server time: query.value(0).toString(); } return 0; }运行后第一行drivers列表里出现QOCI说明插件已被正确加载。如果列表里没有QOCI回到第2章检查文件是否齐全或者参考第4章的排查思路。这里有个连接字符串的细节值得展开。setDatabaseName(//192.168.1.100:1521/orcl)这种写法叫EZCONNECT不需要tnsnames.ora适合调试快速验证。如果你更习惯传统TNS写法就把databaseName设成tnsnames.ora里的别名比如ORCLORCL (DESCRIPTION (ADDRESS_LIST (ADDRESS (PROTOCOL TCP)(HOST 192.168.1.100)(PORT 1521)) ) (CONNECT_DATA (SID orcl) ) )EZCONNECT直观TNS方式适合生产环境统一管理连接描述两种方式各有好处。4. 驱动加载失败类报错的完整排查链路这一节遇到的报错比较集中我把最常见的那几种按出现频率和根因拆开讲。4.1 QOCI driver not loaded七成是插件路径问题这条报错出现率最高。看到它不要先怀疑代码按这个顺序排查第一步确认qsqloci.dll已经放在当前Qt构建对应的plugins/sqldrivers目录下不要放错套件目录第二步打开cmd切换程序目录运行exe看是否还报同样的错在命令行里运行更容易暴露出加载异常第三步检查qsqloci.dll依赖的oci.dll能否被找到。把instantclient_32里的DLL全部复制到exe同目录如果这时驱动能加载说明是PATH没生效而不是插件本身坏了第四步如果仍然不行用Process Explorer查看进程加载的模块列表看qsqloci.dll是否被加载旁边的oci.dll又是否被加载能直接定位断在哪一层。还有一个容易被忽略的问题程序里用QApplication::libraryPaths()指定了自定义插件路径导致Qt不去默认的plugins目录找驱动。检查项目里有没有类似覆盖libraryPaths的代码。如果有要么把plugins/sqldrivers的绝对路径加进去要么删掉覆写逻辑。4.2 找不到oci.dll或提示不是有效的Win32应用程序这个报错十有八九是32位和64位混用。判断方法很直接在cmd里输入where oci.dll看命令输出的路径是不是指向C:\oracle\instantclient_32\oci.dll。如果指向system32目录那你很可能装过64位Oracle客户端system32里那份是64位DLL32位进程死活加载不上。这里有个Windows机制要说明64位系统上system32目录放的是64位DLLSysWOW64目录才是给32位程序用的。很多人把文件“补”进system32结果32位程序去找时反而从SysWOW64里加载文件放在system32根本没人理。正确做法是把32位Oracle客户端放在独立目录并用PATH指定不要往系统目录里塞。另外如果where oci.dll搜出多个路径系统会按PATH里的顺序优先加载第一个。这就是我前面强调PATH第一位置必须放instantclient_32的原因。4.3 ORA-12154与ORA-12514连接描述写法的坑ORA-12154的完整提示一般是TNS:could not resolve the connect identifier specified含义是Oracle解析不出你给的那个“数据库名”。常见原因用的是TNS别名但tnsnames.ora不在TNS_ADMIN指向的目录里写的是ORCL但tnsnames.ora里根本没有这个条目tnsnames.ora里写的是SID orcl而服务端实际用的是Service Name两边对不上。最快的验证方式是直接用EZCONNECT绕过TNS解析db.setDatabaseName(//192.168.1.100:1521/orcl);这样能连上问题一定出在TNS描述EZCONNECT也连不上先ping主机、telnet 1521端口排除网络层问题再看数据库服务名。ORA-12514的提示一般是服务当前不可识别TNS listener does not currently know of service requested in connect descriptor。这通常是服务名写错监听器里没有这个Service Name。用EZCONNECT时尤其要确认URL里的那一段是Service Name而不是实例名必要时登录数据库执行show parameter service_names查询真实服务名。4.4 中文乱码与NLS_LANG设置乱码不是“连不上”层面的问题但比连不上还烦人。通常表现为连接没问题SQL能执行但中文查询结果全是问号。先说结论任何涉及Oracle的工具链里NLS_LANG必须和数据库字符集对齐。Oracle 11常见中文字符集是ZHS16GBK连接时建议显式设置NLS_LANGSIMPLIFIED CHINESE_CHINA.ZHS16GBK设置方式有两种。第一种是在系统环境变量里加NLS_LANG第二种是在连接选项里设置db.setConnectOptions(ORA_NLS_LANGSIMPLIFIED CHINESE_CHINA.ZHS16GBK);留意一点NLS_LANG里含点号有些版本对连接选项的解析比较严格如果setConnectOptions报参数错误就退回环境变量方式反而省心。还有一类乱码是字符集数据文件缺失引起的。如果instantclient_32下没有nls子目录或nls/data里缺少对应语言数据会出现ORA-12705Cannot access NLS data files or invalid environment specified。这种情况下先检查NLS_LANG和客户端自带字符集数据是否匹配再确认没有把oraociei11.dll裁剪掉。字符集数据文件默认就在安装包里剪掉之后中文字符集自然失效。5. 把驱动包随程序发布出去时系统级的那点事本地环境配置完还不够程序是要交给别人用的。发布环节有几个细节我吃过亏专门列出来。5.1 发布目录的完整结构假设最终用户机器是32位Windows发布包里至少需要这几块发布目录/ ├─ YourApp.exe ├─ Qt5Core.dll, Qt5Sql.dll, ... ├─ plugins/sqldrivers/qsqloci.dll ├─ oracle/instantclient_32/ │ ├─ oci.dll │ ├─ oraociei11.dll │ ├─ ... └─ vc_redist.x86.exe有人问既然开发机在C:\oracle\instantclient_32里能用发布时直接让用户装Oracle客户端不就行了理论上可以但让最终用户下载Oracle Instant Client再配置环境变量的成本太高而且Oracle客户端版本一多还会互相打架。更稳妥的做法是程序启动时把Oracle目录加进PATH或者在代码里用LoadLibrary加载绝对路径做成便携式部署。QT的windeployqt工具会帮你把QT基础DLL复制到exe目录但它不认识Oracle那一堆文件。所以发布前要手动把instantclient_32整体复制过去别指望工具自动处理。5.2 干净虚拟机里的最终验证其次是VC运行库。Oracle Instant Client 11.2基于VS2013编译依赖msvcp120.dll和msvcr120.dll你编译QT程序用的MinGW或MSVC则对应另一套运行时比如libstdc-6.dll或vcruntime140.dll。哪怕你用MinGW套件Oracle客户端自带的VS2013运行库依然不能少否则模块加载时照样报错。我的习惯是最后在一台干净虚拟机里部署一遍从点击exe开始用Process Explorer看每一次加载失败凡是加载坏了的模块都能在列表里看出来。花十分钟过一遍比到现场再查划算得多。依赖这东西最怕的不是文件多而是你以为都带了结果漏了关键的那么一两个。本文还有配套的精品资源点击获取
返回列表