简介:一份基于 Delphi 12.3 的 UniDAC 与 FPC 数据库访问控件合集,面向需要在 Object Pascal 环境中实现跨平台数据库连接与开发的程序员,尤其适合使用 Rad Studio 12.3 或 Lazarus/FPC 进行项目构建的中高级开发者。压缩包共收录 2000 个文件,整体约 85.73MB,核心为 996 个 ppu 编译单元与 923 个 o 目标文件,可满足直接链接调用需求;另有 17 个 sql 脚本、54 个 rsj 资源映射文件及 html/pdf/txt 文档,便于查阅接口用法与工程结构。包内预览可见历史记录、帮助说明与库文件列表,说明这份资料既提供可编译链接的预构建产物,也保留了必要的文档索引。已有 125 人学习浏览,适合正在搭建数据库访问层、需要离线控件单元来加速工程编译的 Delphi 开发者,可明显减少手动适配与查找接口的时间成本。
1. Delphi 12.3 下的 unidacfpc.zip 是什么:一份源码同时喂给两套 IDE
Delphi 12.3 的开发者第一次看到 unidacfpc.zip 时通常会愣一下:fpc 不是 Lazarus 那边的东西吗,跟 Delphi 有什么关系?实际工作中我同时维护一个 Delphi 12.3 写的 Windows 服务端和一个 Lazarus 编译的客户端工具,两边都要访问 MySQL 和 SQLite,数据库访问逻辑如果各写一套,改一个字段名就要同步两处。unidacfpc.zip 的价值恰好在这里:它装的是 UniDAC 的源码版,核心组件仍然是 TUniConnection、TUniQuery、TUniScript 这一套,但包工程同时带了 .dpk(Delphi 用)和 .lpk(Lazarus 用)。也就是说,连接参数、事务代码、SQL 文本可以跨两套 IDE 保持一致。适合手里正好有 Delphi 12.3 和新老 FPC 项目、想把数据访问层统一起来的人。这篇笔记按安装、连接配置、交叉编译、排错的顺序写,照着走完,本机就能跑起最小示例。
2. 把 unidacfpc.zip 装进 Delphi 12.3:解压、Library 路径与包注册
2.1 为什么 Delphi 12.3 要源码版而不是预编译版
UniDAC 在 Devart 官网提供两种形态:安装程序版和源码 zip 版。unidacfpc.zip 从命名看属于后者,并且带了 fpc 后缀,说明它额外保留了 Lazarus 包工程。这里有个关键选择:Delphi 12.3 的 RAD Studio 对 BPL 包的版本检查很严格,预编译版里附带的 DCU/BPL 是针对特定 Delphi 版本生成的,直接拿来 Add 进 12.3,很容易碰到 “Package ... was compiled with a different version of ...” 这类错误。源码版拿到手的是 .pas、.dpk、.lpk,没有字节码兼容性问题,遇到新 IDE 自己重编一遍即可。
我一般不建议在 Delphi 12.3 里强行安装给旧 Delphi 做的预编译安装包。源码版多花两分钟编译,换来的是路径可控、可查、回退方便。这个 zip 里既有 .lpk 又有 .dpk,意味着 Lazarus/FPC 和 Delphi 12.3 共用同一批 Source 下的 .pas 文件,业务层代码不会因为 IDE 不同而分叉,这是它比安装程序版更适合跨平台团队的核心原因。
2.2 解压后的目录结构和 Library 路径配置
把 zip 解压后,先别急着打开 IDE,花两分钟看清目录结构。常见布局是四块:
| 目录 | 作用 | 安装时怎么用 |
|---|---|---|
| Source | 核心单元,例如 UniDAC.pas、UniConnect.pas | 加入 Delphi 的 Library path 和 Browsing path |
| Packages | 按 IDE 分放的 .dpk / .lpk 工程 | Delphi 编译 .dpk,Lazarus 编译 .lpk |
| Demos | 各种数据库的示例工程 | 对照学习连接参数,排错时抄配置 |
| Docs | 参数说明文档 | 查 SpecificOptions 各驱动支持项 |
下一步是把 Source 路径告诉 Delphi 12.3。打开 IDE 后进入 Tools > Options > Environment Options > Delphi Options > Library,把解压目录下的 Source 完整路径加到 Library path。这一步的细节决定后面会不会花一小时找错:路径写到...\unidacfpc\Source这一层就够,不用再往下钻到子目录;另外把同一路径也加到 Browsing path,否则编辑器跳转和代码提示不认,编译时报的却是 “Unit not found: UniDAC”。
提示:Library path 管编译期查找单元,Browsing path 管编辑器导航。两条都填,后面少折腾。
2.3 编译并注册 .dpk 到组件栏
路径配好后,执行 Component > Install Packages > Add,定位到 Packages 目录下对应 Delphi 12.3 的 .dpk 文件。这里有一个常见分歧:有人直接 Add,有人先 Open 再 Build。我常用的是先把 .dpk 打开,右键 Project > Build,确认编译零错误后再 Install。Add 之后再编译一旦报错,IDE 会进入半注册状态,反而难收拾。
编译时如果提示缺某个 unit,九成是 2.2 的 Library path 没配好,回到那里检查;如果提示某某符号重名,通常是本机以前装过其他版本的 UniDAC,残留的 BPL/DCP 还在路径里,后面第 5 章会专门讲。注册成功后,Tool Palette 里搜 TUni,能看到 TUniConnection、TUniQuery、TUniScript、TUniSQLMonitor 等组件,到这一步安装就算完成了。
2.4 最小连接测试:用 SQLite 验证整条链路
装完组件不能只看图标,跑一个最小连接最稳。我习惯先用 SQLite 做验证,因为它不需要任何外部客户端库,环境问题最少。放一个按钮,写这段代码:
procedure TForm1.ButtonConnectClick(Sender: TObject); var DB: TUniConnection; begin DB := TUniConnection.Create(nil); try DB.ProviderName := 'SQLite'; DB.Database := 'C:\temp\test.db'; DB.SpecificOptions.Values['Direct'] := 'True'; DB.Connect; ShowMessage('连接成功,版本:' + DB.ServerVersion); finally DB.Free; end; end;这段代码主要做三层验证:其一,ProviderName 能不能被 IDE 正确识别,如果包没注册好,这里会直接编译报错;其二,SpecificOptions 的键值写法对不对,UniDAC 用这种方式传驱动独立参数,不是每个属性都有可视化编辑器;其三,ServerVersion 在连接成功后才有效,出现异常说明连接链路本身有问题。拿 SQLite 跑通后,再切 MySQL 或 PostgreSQL,后续的排错范围就小很多。
3. 连接配置的落地参数:ProviderName、直连模式和字符集怎么设
3.1 ProviderName 先选对,连接信息才有意义
UniDAC 的连接入口有两条路:一条是 TUniConnection 上独立的 Server、Port、Database、Username、Password 属性,另一条是 ConnectString 字符串。两条路最终都汇到 ProviderName 决定的驱动上。实际落地时我建议先设置 ProviderName,原因很直接:设计期下拉框、属性编辑器、SpecificOptions 的提示列表都依赖这个值,ProviderName 选错,后面所有参数提示都会跟着错位。
ProviderName 的取值和驱动对应关系,常用的列一张表:
| ProviderName | 对应数据库 | Direct 模式注意事项 |
|---|---|---|
| SQLite | SQLite 3 | 无需外部 DLL,最省事 |
| MySQL | MySQL / MariaDB | Direct=True 仍需 libmysql.dll 或 libmariadb.dll |
| PostgreSQL | PostgreSQL | Direct=True 一般需要 libpq.dll |
| Oracle | Oracle | 多数环境需要 OCI 相关库,注意 32/64 位匹配 |
| SQL Server | Microsoft SQL Server | 通常走 OLEDB/ODBC 桥接,按目标机位数配置 |
| Firebird | Firebird / InterBase | 通常需要 fbclient.dll |
这里有个容易踩的命名坑:UniDAC 里 SQL Server 的 ProviderName 写成'SQL Server',不是常见的'MSSQL'或'Microsoft SQL Server'。写错时 IDE 不报错,运行时才提示找不到 Provider,属于典型的黑匣子问题。
连接字符串写法与属性赋值不同,属性用 Server,字符串里用 Data Source。我常用的写法是:
UniConnection1.ProviderName := 'MySQL'; UniConnection1.ConnectString := 'Provider Name=MySQL;Data Source=127.0.0.1;Port=3306;' + 'Database=inventory;User ID=dev;Password=dev123;Charset=utf8mb4';这段代码里 ConnectString 是一个分号分隔的键值串,键名和 TUniConnection 的属性名不完全一致:Data Source 对应 Server 属性,User ID 对应 Username 属性。新手往往照属性名去写,结果驱动识别不出来。我一般把 ConnectString 用于配置文件持久化,把属性赋值用于设计期快速切换,两种方式混用时要确认最终以哪份为准,避免代码里赋值一套、配置文件里又一套。
3.2 直连模式:什么时候开,什么时候关
UniDAC 的 Direct 模式是它区别于很多数据库组件的卖点,但这个词经常被误解成“免装客户端”。Direct 模式只是在库层面绕过了厂商的中间层,底层 C 库该加载还是会加载。以 MySQL 为例,把 SpecificOptions 里的 Direct 设成 True,连接时仍然需要 libmysql.dll 或 libmariadb.dll 在场,否则初始化直接失败。
UniConnection1.ProviderName := 'PostgreSQL'; UniConnection1.Server := '10.0.0.8'; UniConnection1.Port := 5432; UniConnection1.Database := 'erp'; UniConnection1.Username := 'app'; UniConnection1.Password := 'app123'; UniConnection1.SpecificOptions.Values['Direct'] := 'True'; UniConnection1.Connect;为什么常见做法是保留 Direct 开关而不是让它常开?因为在正式内网环境里,很多团队已经部署了厂商客户端并维护了 tnsnames.ora、pg_hba.conf 等配置,这时候走客户端库的连接名解析,比在每台机器上手工指定主机端口更省力。Direct 模式适合的场景是:临时测试、交付给不带客户端的现场、以及 CI 环境里跑自动化用例。这里有个血泪经验:现场机器如果位数是 32 位而 exe 是 64 位,DLL 加载失败是必然的,后面第 5 章再展开。
3.3 字符集、UseUnicode 与事务边界
UniDAC 连接里的文字乱码,大多数不是控件坏了,而是字符集参数没有对齐。Delphi 12.3 的 string 是 UnicodeString,UniDAC 在内部会把字符串转成目标数据库客户端库期望的编码。如果连接串里不指定 Charset,MySQL 默认按 latin1 走,中文读写就变成问号和乱码。我在连接串里固定写Charset=utf8mb4,MySQL 8 以上尤其要坚持,时不时的编码玄学基本就此消失。
事务边界同样属于连接级参数。TUniConnection 默认 AutoCommit 为 True,简单查询无所谓,但有写入操作时我建议显式关掉:
UniConnection1.AutoCommit := False; try UniConnection1.StartTransaction; UniQuery1.SQL.Text := 'insert into orders(id, amount) values(:id, :amount)'; UniQuery1.ParamByName('id').AsInteger := 1001; UniQuery1.ParamByName('amount').AsFloat := 299.9; UniQuery1.ExecSQL; UniConnection1.Commit; except UniConnection1.Rollback; end;AutoCommit 设为 False 后,连接上的写操作会进入未提交状态,直到 Commit 或 Rollback。这个行为在 Delphi 12.3 和 FPC 侧是一致的,恰好符合“一份源码两套 IDE 共用”的初衷。事务与字符集参数都建议在连接建立前一次配完,不要指望连接已经打开后再改,UniDAC 对运行中的参数变更支持有限,改完经常要重连才生效。
4. 在 FPC/Lazarus 侧复用同一份源码:两组编译参数与代码分支
4.1 用 .lpk 安装与命令行编译的路径参数
前面装的是 Delphi 12.3 侧,这一节说 Lazarus/FPC 侧怎么接手同一份源码。Lazarus 的包管理与 Delphi 不同,走的是菜单 Package > Open Package File,文件类型选 *.lpk,在 unidacfpc 的 Packages 目录下找到对应版本后打开,先 Compile 再 Install。安装成功后,新建工程时检查 Project > Project Options > Paths > Other unit files 里是否自动加入了 Source 路径。Lazarus 有时不会自动追加,没有就手动加,这一步漏掉的话,FPC 编译器找不到 UniDAC 的单元。
如果你习惯用命令行编译,最小命令大致长这样:
fpc -MObjFPC -g -O1 \ -Fu"E:\dev\unidacfpc\Source" \ -Fu"E:\lazarus\lcl\units\x86_64-win64" \ uniTest.dpr-Fu 是 FPC 的单元搜索路径参数,可以多次出现,路径含空格时用双引号包起来。-MObjFPC 指定的是 Object Pascal 语法模式,Lazarus 默认用这个模式编译,不加的话遇到带 class 关键字和异常处理的代码会报语法错误。-g 生成调试信息,-O1 开基础优化。路径里的 x86_64-win64 取决于 Lazarus 配置的目标 CPU,32 位工具链对应的是 i386-win32。这条命令的作用是验证 unidacfpc 的单元能被 FPC 正确找到,编译输出一致后,再回到 IDE 里做后续开发。
常见做法是先用 .lpk 在 IDE 里跑通,再用命令行做持续集成。命令行编译对路径顺序敏感,如果本机同时装了多个版本的 Lazarus,-Fu 的顺序必须是 unidacfpc 在前、LCL 在后,避免编译器取到旧的 DCU。
4.2 Delphi 12.3 与 FPC 的字符串差异:写分支而不是写两套函数
Delphi 12.3 的 string 默认是 UnicodeString,FPC 在多数模式下默认是 AnsiString。两边处理同一段中文字段时,会出现一侧正常、一侧乱码的现象,而且问题不一定出在 UniDAC,而是 string 类型语义不同。我在共享源码里用条件编译直接切分:
var S: string; begin {$IFDEF FPC} S := UTF8ToAnsi(Trim(qry.FieldByName('name').AsString)); {$ELSE} S := Trim(qry.FieldByName('name').AsString); {$ENDIF} end;这段代码的意义在于:FPC 分支里显式把 UTF-8 转成 Ansi,Delphi 分支保留原生 UnicodeString。不要在两边都写AnsiString强转,Delphi 12.3 下强转会丢字符,FPC 下不转又可能乱码。{$IFDEF FPC} 是 FPC 预定义的条件编译符号,Delphi 编译器会忽略 FPC 分支里的代码,两边互不干扰。类似的差异还体现在字符串拼接和 Chr 函数上,凡是和外部系统交换数据的入口,我都用独立函数封装,中间加一个条件编译分支,不要散落在业务代码里。
4.3 FetchAll 和游标取舍:报表与在线浏览是两种玩法
TUniQuery 的 FetchAll 属性是 UniDAC 在结果集处理上的一个关键开关。默认情况下,查询是边取边用的流式游标,对大结果集内存友好;但如果把 TUniQuery 直接绑给 DBGrid,或者在一次遍历里又去执行别的 SQL,游标状态会变得微妙,偶发异常时很难一眼定位。
我的习惯是两种场景分开处理:报表、导出、批量计算这种“一次全取”的任务,把 FetchAll 设成 True,行为确定,遍历多少行就是多少行;在线分页浏览则设 False,配合 SQL 层的 Limit/Offset 做物理分页,不要让一个连接长期挂着一个大结果集不释放。FetchAll 的设置直接写在 TUniQuery 上:
UniQuery1.FetchAll := True; UniQuery1.SQL.Text := 'select id, name, created_at from orders where created_at >= :d1'; UniQuery1.ParamByName('d1').AsDateTime := IncDay(Now, -7); UniQuery1.Open;这段代码先决定 FetchAll 再执行 SQL,是因为 FetchAll 在 Open 之前设置才生效。参数用 ParamByName 绑定而不是拼 SQL 字符串,既避开日期格式坑,也让 FPC 和 Delphi 两侧的代码保持一模一样。Delphi 12.3 和 FPC 对这个属性的行为一致,跨 IDE 切换时不产生额外维护成本。
5. unidacfpc 的常见坑与排查:从重复安装到 DLL 缺失的 5 个实录
5.1 安装期:重复注册、路径残留与编译错乱
坑 1:重装或升级后 IDE 报 TUniConnection 已注册。
现象:打开包管理器看到同类组件出现两条记录,把新包 Add 进去时 IDE 直接拒绝,提示组件名已存在。
原因:旧版本的 BPL/DCP 文件残留在 IDE 的库目录里,Delphi 12.3 在启动时把这些旧包也加载了,新包的同类组件注册时发生名称冲突。
解决:先到 Component > Install Packages 里移除所有 UniDAC 相关项,关闭 IDE,删除%LocalAppData%\Embarcadero\BPL或对应版本目录下的旧 BPL/DCP 文件,再重新打开 IDE 安装新包。删除前确认只删 UniDAC 相关文件,不要碰其他控件的 BPL。
坑 2:Library path 加了却仍然报 Unit not found: UniDAC。
现象:代码里 uses UniDAC,编译时 IDE 提示找不到单元,但回到 Tools > Options 查看路径确实已经填了。
原因:路径填到了 Browsing path 那一行,或者填对了行但 Source 路径写到子目录层级不对。Library path 才是编译期查找单元的地方,Browsing path 只服务编辑器和代码提示。
解决:打开 Tools > Options > Environment Options > Delphi Options > Library,把...\unidacfpc\Source完整路径放进 Library path 输入框,而不是 Browsing path。补好后先关闭再重开工程,IDE 对 Library path 的缓存有时不会立刻刷新。
5.2 运行期:DLL 缺失、位数错配、字符集乱码
坑 3:MySQL 连接初始化失败,报找不到 libmysql.dll 或 libmariadb.dll。
现象:ProviderName 设为 MySQL,SpecificOptions 里 Direct=True,连接时抛异常,错误信息指向某个 DLL 文件不存在。
原因:Direct 模式只是绕过客户端中间层,仍然需要调用 MySQL 的 C 客户端库;而且 DLL 的位数必须和 exe 一致,Delphi 12.3 编译出的 64 位程序配 32 位 DLL,加载必然失败。
解决:把对应版本的 libmysql.dll 或 libmariadb.dll 放到 exe 同目录,或加入系统 Path。先确认 exe 是 64 位还是 32 位,再选同位数 DLL。经验是优先和线上 MySQL 版本匹配,不要为省事从网上下个来路不明的 DLL,运行期诡异问题多半出在这里。
坑 4:插入的中文变成问号或乱码,查询结果也对不上。
现象:界面录入中文,提交到 MySQL 后库里存的是 ???,重新读出来也是乱码。
原因:连接字符集没有对齐。MySQL 默认连接字符集可能是 latin1,Delphi 12.3 侧传的是 UnicodeString,两边转换没有统一到 utf8mb4。
解决:连接串里显式加Charset=utf8mb4,同时检查 UniDAC 的 UseUnicode 选项是否按项目要求打开。改完必须重连,已建立的连接不会自动应用新字符集。SQLite 场景则确认数据库文件本身的编码,UniDAC 侧保持一致即可。
坑 5:Direct 模式连不上,客户端模式反而正常。
现象:测试环境把特定数据库的 Direct 设为 True,报错提示找不到某个客户端库;去掉 Direct 后连接成功。
原因:PostgreSQL 的 Direct 模式依赖 libpq.dll,Oracle 的直连依赖 OCI 相关组件,这些库不是每台机器都有,也不是每个版本都兼容。
解决:检查目标机的 C 库是否安装、位数是否匹配。如果现场环境已经部署了厂商客户端,干脆保持 Direct=False,让 UniDAC 走已存在的客户端配置。交付内网环境时,后者通常比临时找 DLL 更省心。
排查通用手段:出现上述运行期问题时,我一般先开 TUniSQLMonitor,把连接过程中的 SQL 和状态事件打印到日志,再下结论。
procedure TForm1.UniSQLMonitor1SQL(Sender: TObject; Text: string); begin Memo1.Lines.Add(FormatDateTime('hh:nn:ss', Now) + ' ' + Text); end;TUniSQLMonitor 是 UniDAC 自带的事件型追踪组件,OnSQL 事件在驱动层执行 SQL 时触发,Text 参数里能看到实际下发的语句和错误上下文。把这段日志和上面几个坑对照,通常几分钟就能定位到 DLL、字符集还是事务边界的问题。
6. 进阶调试:用 TUniSQLMonitor 给连接过程留一份可回放日志
前面第 5 章已经提了 TUniSQLMonitor 的基础用法,这里再往前推一步:把它做成一个按天轮转的日志文件,让现场问题有据可查。用 UniDAC 连数据库的服务,最怕用户报一句“连不上”但又说不出完整报错。给程序加一个可开关的日志记录,接上 monitor 事件,现场就能留下一份可回放的连接时间线。
procedure TForm1.LogSQL(Sender: TObject; Text: string); var F: TextFile; LogName: string; begin LogName := 'unidac-' + FormatDateTime('yyyymmdd', Now) + '.log'; AssignFile(F, LogName); if FileExists(LogName) then Append(F) else Rewrite(F); try WriteLn(F, FormatDateTime('hh:nn:ss.zzz', Now) + ' | ' + Text); finally CloseFile(F); end; end;这个片段把日志文件按天切分,同一天内走追加,跨天自动换新文件。yyyy-mm-dd 格式直接体现日期,小时分钟带上毫秒,排错时能精确对应到某一次连接动作。正式发布时,我给界面留一个注册表开关控制日志开关;现场出问题,让对方把 unidac-日期.log 发回来,多数时候填好感,再对照日志里最后的失败动作,问题就落到具体参数上。这个习惯我保留了很多项目,比现场远程调试省力得多。希望帮到你。
本文还有配套的精品资源,点击获取