ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Delphi下ODAC连接Oracle实战:版本兼容、Direct模式与避坑指南

Delphi下ODAC连接Oracle实战:版本兼容、Direct模式与避坑指南 简介面向 Delphi 6 至 Delphi 13 开发者的 Devart ODAC v12.0.2 完整源代码包专用于在 Delphi 应用程序中高效接入 Oracle 数据库解决原生组件连接慢、功能覆盖不足等问题。该组件集合支持 Oracle 12c 等新特性提供数据绑定、数据感知控件、数据集操作及跨平台部署能力适用于管理信息系统、ERP、金融等企业级应用场景尤其适合需要深度定制数据访问层的中高级开发人员。压缩包共两千个文件大小约九点七七兆主要包含一百八十三个 Pascal 单元、二百六十五个包工程、四百三十九个头文件以及窗体、资源文件等目录结构清晰可直接在 Delphi IDE 中编译安装使用。已有一百八十人学习浏览。通过完整源码读者既能快速集成 ODAC 缩短开发周期也能深入剖析组件内部机制按需修改数据访问逻辑同时获得 Devart 持续的错误修复与性能优化支持为长期维护 Oracle 数据库应用提供可靠保障。1. 为什么Delphi 13.1还得靠Devart.ODACOracle连接不只是连上这么简单做过Delphi连接Oracle的人应该都有体会用原生OCI写一个能跑的连接程序不难但要在D6到D13这么多版本里维护同一套代码还要兼顾32位和64位、Direct模式和OCI模式、Unicode和Ansi字符集这就不是一个“能用”的控件能满足的事了。Devart.ODAC v12.0.2 for D6-D13 Full Source这套资源解决了两个最实际的问题一是带完整源码出了诡异问题你可以自己翻单元排查二是它一口气覆盖了从Delphi 6到Delphi 13的版本跨度意味着你不用为了某个老项目单独保留一套旧环境。对于还在维护老系统的工程师或者正在往新版本迁移的团队这套控件几乎是把Oracle访问层的“后悔药”直接打包好了。我拿到这套资源后的第一反应是看它的单元文件命名和版本分支确认它确实是按不同Delphi版本做了目录拆分的。实际用下来装完之后最值的不是那个设计期包而是源码里那几份接口文档和示例工程。如果你正在经历“Oracle连接偶尔断一下重启后又好了”这种玄学问题或者被TNS配置折磨到头大这篇笔记把安装、配置、常用组件参数和避坑记录一次性说清楚。2. 选型先看版本矩阵D6到D13的边界条件与License限制2.1 版本目录结构与Delphi版本对应关系解压后的目录结构直接决定了你能不能装对包。ODAC 12.0.2的源码包通常在根目录下按编译器版本拆分常见的如“D6”“D7”“D10_3”“D11”“D12”等子目录。每个目录里放的是对应编译器版本的设计期包和运行期包源文件。我一般会先确认当前Delphi版本对应的目录名Delphi 10.3 Rio对应D10_3Delphi 11 Alexandria对应D11Delphi 12 Athens对应D12Delphi 13.1则对应D13或D13_1。注意这套资源标题写的是D6-D13实际上覆盖到Delphi 13.x但如果你用的是中间版本比如Delphi 10.4需要看一下有没有对应目录没有的话可以选D10_3的包在IDE里强行编译试试多数时候能编译过但设计期包可能注册不上去。# 解压后先看目录结构 ls -la | grep ^d # 输出示例 drwxr-xr-x d10_3 drwxr-xr-x d11 drwxr-xr-x d12 drwxr-xr-x d13 drwxr-xr-x d6 drwxr-xr-x d7这个操作的逻辑是先确认当前Delphi主版本有没有对应目录再用grep排除掉文档和示例目录缩小编译目标的搜索范围。参数就是目录名和Delphi版本的映射不要凭文件名猜。如果找不到对应版本目录不要硬装。我试过把D11的包往D12里装结果IDE报“Package ... is not compatible with this Delphi version”原因很简单包文件头里的编译器版本标识对不上。解决方法是找到源文件后在IDE中手动新建一个包工程把运行期单元加进去重编过程不复杂但要花时间。2.2 License机制与设计期运行期分离ODAC一直区分设计期Design Time和运行期Runtime授权。Full Source版本给你的是全部源码但License文件决定了你在哪台机器上能用。安装时它会往系统目录或注册表里写入授权信息重装系统后需要重新激活。我遇到过一次诡异的情况把编译好的BPL和DCP拷到另一台机器运行期程序正常跑但IDE里打开窗体就会报“License not found”。原因就是设计期组件需要本机授权运行期不需要。解决方法是把安装生成的license文件一起拷过去或者在那台机器上重新跑一遍安装程序。注意如果你只是部署已编译好的exe不需要在目标机器装License。但如果要在IDE里拖控件、改属性每台开发机都要有授权。2.3 64位与32位平台的选择ODAC 12支持64位编译但要注意源码包里有些第三方依赖单元只给了32位版本比如部分加密组件。如果你的项目需要64位部署先测试一下TNS模式连接因为64位应用程序连Oracle时OCI DLL必须匹配64位。// 在代码中判断当前平台便于排查连接问题 {$IFDEF WIN64} ShowMessage(64-bit ODAC build); {$ELSE} ShowMessage(32-bit ODAC build); {$ENDIF}这段代码用条件编译区分平台版本目的不是做功能分支而是当连接报“OCI.dll was not found”时先确认当前exe是哪个位数的再决定去找32位还是64位的OCI。常见坑是系统里装了32位Oracle Client但exe是64位编译ODAC找不到对应位数的OCI就会报错。3. 从源码包到IDE组件栏的完整安装流程常见做法是源码编译兜底3.1 安装前准备确认Delphi版本与目录映射拿到压缩包后不要直接解压就双击安装脚本。ODAC的安装器会检测当前IDE版本但Full Source包更常见的用法是手动编译源码安装。原因很简单Full Source目标用户往往要改源码直接把源码注册进IDE反而方便后续调试。我一般会先备份当前IDE的组件配置。Delphi的组件注册信息存在注册表里具体位置和编译器版本有关可以在IDE的“Component Install Packages”里把现有包列表截图万一装坏了可以对照恢复。# 备份IDE包列表Windows下用regedit导出 regedit /e C:\backup\delphi_packages.reg HKEY_CURRENT_USER\SOFTWARE\Embarcadero\BDS\23.0这段命令把Delphi 11的包注册表信息导出备份。路径里的23.0是BDS版本号Delphi 11对应23.0Delphi 12对应24.0Delphi 13对应25.0左右如果不是标准路径先在注册表里搜“Embarcadero”确认。导出后就算装出问题双击reg文件就能恢复。3.2 编译运行期包手动编译的三个关键参数打开Delphi后打开源码目录里的运行期包工程通常是OraDAC_Run.dpk在Project Options里要确认三个参数一是输出路径指向你希望的BPL输出目录二是DCP输出目录要和BPL一致三是“Build Control”选“Rebuild”而不是“Compile”因为源码包里的DCU可能是旧版本编译的直接Compile可能不会重新生成。# 编译命令示例前提是已打开dpk并设置好输出路径 # 在IDE中按 CtrlF9 编译当前包 # 编译成功后会生成 OraDAC_Run.bpl 和 OraDAC_Run.dcp编译时如果报找不到某些单元多半是源码路径没有加入Library Path。ODAC源码里有很多子目录根目录下的Source、Source\Oci、Source\MemUtils等都要加进IDE的Library Path。3.3 安装设计期包并验证组件栏运行期包编译通过后再打开设计期包通常是OraDAC_Design.dpk或类似名称设计期包依赖运行期包所以顺序不能反。设计期包编译并Install后组件栏会出现TOraSession、TOraQuery、TOraTable等组件。// 新建一个测试工程放一个TOraSession验证安装 procedure TForm1.FormCreate(Sender: TObject); begin OraSession1.Options.Direct : True; // Direct模式不依赖Oracle Client OraSession1.ConnectString : Data Source//192.168.1.10:1521/orcl;User IDscott;Passwordtiger; OraSession1.Connect; end;这里的关键是把Direct设为True。ODAC的Direct模式自己实现了Oracle协议不需要本机装Oracle Client。对有洁癖的人来说这是福音——你只需要知道数据库的IP、端口和SID就能连上。ConnectString的格式是标准的EZ Connect语法注意User ID和Password区分大小写。3.4 安装失败的三个典型表现安装过程中最容易翻车的点是包依赖顺序。运行期包没编译通过就去装设计期包会直接报“Cannot find package ...”。另一个常见问题是路径含中文或空格有些源码在编译时对路径处理不严谨路径里有空格会生成失败。第三是64位系统下IDE是32位进程装的组件包也是32位但你的项目是64位目标这时设计期能拖控件编译64位exe时却连接不上运行期库。现象是编译时报“Unable to find package OraDAC_Run”但运行期包明明装好了。原因就是64位项目需要64位版本的运行期包。解决方法是把源码里的运行期包重新配置为64位目标编译一遍。4. 核心组件实战TOraSession参数、TCRQuery用法与数据映射4.1 TOraSession的连接参数详解与Pooling设置TOraSession是ODAC里最核心的会话组件几乎所有其他组件都挂在他下面。连接参数里最常用的几个ConnectString、Options.Direct、Options.EnableIntegrity、Pooling。// 推荐的连接配置方式 with OraSession1 do begin Options.Direct : True; Options.Pooling : True; // 启用连接池 PoolingOptions.MaxPoolSize : 20; PoolingOptions.MinPoolSize : 2; PoolingOptions.ConnectionLifetime : 300; // 秒 ConnectString : Data Source//db-server:1521/orcl;User IDapp_user;Passwordpass; Connect; end;这里Pooling的关键参数是MaxPoolSize和ConnectionLifetime。MaxPoolSize太高会占用数据库端连接资源通常并发用户数乘1.2就够了。ConnectionLifetime设为300秒让空闲连接在5分钟后释放避免数据库端出现太多死连接。注意Pooling只在Direct模式下有效OCI模式下Pooling选项会被忽略。4.2 TOraQuery的SQL执行与游标控制TOraQuery继承自TDataSet用法和Delphi自带的TQuery类似但多了Oracle特定选项比如FetchAll、TruncateChar、ArraySize。// 大数据量查询时关闭FetchAll配合FetchRows分批取 with TOraQuery.Create(nil) do try Session : OraSession1; SQL.Text : SELECT * FROM big_table WHERE create_date :start_date; ParamByName(start_date).AsDateTime : EncodeDate(2024, 1, 1); FetchAll : False; // 分批取数据避免一次性占用大量内存 FetchRows : 500; // 每批取500行 Open; while not Eof do begin // 逐条处理数据 ProcessRow(Fields[0].AsString); Next; end; finally Free; end;FetchAll设为False是这个场景的关键千万行级别的表如果一次性FetchAll内存直接爆炸。FetchRows设500是一个平衡值取少了网络往返次数增加取多了内存压力大。ODAC在这里做了预取优化Next游标移动时不会每次都访问数据库而是在本地缓冲区里拿数据。处理完循环后务必调用FreeODAC的TOraQuery持有Oracle游标不释放的话数据库端游标数会涨到上限报ORA-01000。4.3 数据写入与事务控制的边界条件ODAC的TCRQuery组件在写入时和查询有一些关键区别。CRUD操作用TCRQuery会更稳定因为它针对DML语句做了专门的缓冲处理。// 用TCRQuery做批量更新 with TCRQuery.Create(nil) do try Session : OraSession1; SQL.Text : UPDATE orders SET status :st WHERE order_id :id; for i : 0 to OrderList.Count - 1 do begin ParamByName(st).AsString : SHIPPED; ParamByName(id).AsInteger : OrderList[i]; Execute; end; finally Free; end;这个场景下每次Execute都会自动提交还是需要手动提交取决于TOraSession的AutoCommit属性。ODAC默认AutoCommit是True也就是说每条Execute自动提交。如果业务要求要么全成功要么全失败需要把AutoCommit设为False然后用StartTransaction和Commit包住。OraSession1.AutoCommit : False; try OraSession1.StartTransaction; // 这里执行多条TCRQuery OraSession1.Commit; except OraSession1.Rollback; raise; end;这里的事务边界一定不能包错。ODAC的StartTransaction之后所有挂在同一Session下的查询组件都纳入同一个事务包括TOraQuery和TCRQuery。如果中间有组件用了自己的Session那个连接上的操作不受事务控制提交失败时不会回滚。4.4 字段映射与Unicode字符集处理Oracle数据库的字符集如果配的AL32UTF8Delphi这边的String默认是AnsiString直接读取会中文乱码。ODAC通过TWMCharSet属性控制字符集映射。// 连接后设置客户端字符集 OraSession1.CharSet : UTF-8; OraSession1.NCharCharset : UTF-8;设置CharSet后ODAC会把从数据库取出的UTF-8字节转成Delphi字符串的编码。这个转换的代价是CPU开销但对中文用户来说必须设否则字符串长度和数据内容都会不对。另一个坑是VARCHAR2字段里存的长度是字节数一个中文字符占3个字节UTF-8下你用Length统计时得到的是字符数还是字节数取决于ODAC的字段类型映射取出来的AsString是已经转换好的字符直接用String类型接收即可。5. 避坑专题ODAC使用中四个最值得记录的实战问题5.1 现象连接串用SID方式报ORA-12514用Service Name方式正常原因Oracle 12c以后默认使用Service Name注册监听SID在很多环境里已经不是公开信息。ODAC的ConnectString里如果写Data Sourcedb-server:1521:orclSID方式监听找不到对应SID就报12514。解决改用EZ Connect方式Data Source//db-server:1521/orcl或者确认数据库的SERVICE_NAME后写入TNS格式。5.2 现象Direct模式下Oracle 19c连接失败OCI模式正常原因ODAC 12.0.2对Oracle 19c的认证协议支持不完整。19c默认的密码验证方式在某些版本里是SHA-1ODAC的Direct模式用的协议版本如果不对就会在认证阶段被拒。解决如果必须用Direct模式把数据库的密码验证方式改回SQLNET.ALLOW_WEAK_CRYPTO1或者在连接串里加AuthenticationType0强制走旧协议。但最稳的方案是装Oracle Instant Client走OCI模式同时把ODAC的OCI_VERSION参数配到对应版本。5.3 现象应用运行几小时后所有连接突然全部失败报ORA-02396原因数据库端的Resource Limit把每用户的并发连接数上限限制到了某个值比如默认的profile限制为4个并发会话。ODAC的Connection Pool没有正确释放连接池里的连接数越积越多最终撞到limit。解决一是调大数据库profile的SESSIONS_PER_USER二是检查代码里是否所有TCRQuery都及时Free了——尤其注意异常分支里没有Free的查询那些游标和连接永远不会释放。5.4 现象编译时提示“Unit OCIDA32 was compiled with a different version of System.SysUtils”原因Full Source包里有些DCU文件是用旧版本Delphi编译的残留的DCU和你当前IDE版本的系统单元版本不一致。解决在IDE里执行“Project Clean”清除旧DCU然后完整重编。这里有个细节只Compile不Build不会清理旧DCU必须选Build全量编译。另外检查搜索路径里是否包含了多个版本的ODAC源码目录即使不引用同名单文件在搜索路径里出现两次也会造成类似问题。6. 源码级二次开发Direct模式下性能参数调优与自定义加密流ODAC的Full Source价值在标准使用场景下体现不出来但如果遇到性能瓶颈或特殊认证需求直接读代码改参数比外部绕路快得多。我常用的两个进阶方向一是调整Direct模式下的网络缓冲参数二是集成自定义的加密算法。先看网络缓冲。Direct模式完全自己走TCP协议Oracle的TCP包发送缓冲区和接收缓冲区大小直接影响大表全扫的速度。在源码里找到TByteReader的缓冲分配逻辑默认块大小通常是8192字节你可以改成65536字节来减少网络往返次数代价是单次内存占用变大。// 修改后的缓冲读取逻辑示例位置ODAC源码 OCIUtils.pas 的 TByteReader.Read var LocalBuf: PByteArray; Need: Integer; begin // 原逻辑是固定读取8K这里改成动态判断 Need : BufferSize; if Need RemainingBytes then Need : RemainingBytes; GetMem(LocalBuf, Need); try FStream.Read(LocalBuf^, Need); Move(LocalBuf^, Dest^, Need); finally FreeMem(LocalBuf); end; end;这个修改的思路是提前分配好目标缓冲区然后一次性从TCP流里读取需要的字节数。缓冲区大小我习惯设为64KB太小则网络往返太频繁太大则GC压力和内存占用都上去了。再看自定义加密。ODAC的Direct模式默认做了简单的XOR混淆不是真正的加密。如果你的生产和测试环境之间需要过公网连接数据在网络上可以被抓包直接看到SQL明文。我一般会在源码里的TNetSend和TNetRecv方法之间插入一层加密流。// 自定义加密流的接口约定 type TCustomCodec class public class function Encode(const S: TBytes): TBytes; static; class function Decode(const S: TBytes): TBytes; static; end;这个方案适合团队内统一改造客户端服务器端如果有第三方工具接入会直接握手失败。改造时注意别动包头的长度字段只对包体做加解密否则ODAC双方的长度校验会对不上连接会被错误地重置。我在一个生产项目里这么改过改造后抓包只能看到乱码但性能下降了大约15%因为每包都多了一步AES计算。如果在意性能可以考虑只对敏感SQL和返回结果集做加密对普通的查询元数据包不处理。从那以后我每次拿到新的ODAC源码包第一件事就是先看OciUtils里的读写方法确认这版本的缓冲逻辑长什么样再决定要不要动。希望这些从安装踩坑到源码改造的笔记能帮到你省去你对着茫茫多的.pas文件无从下手的时间。本文还有配套的精品资源点击获取
返回列表