简介:ZeosLib 6.0.12 最新稳定版源码包,面向使用 Delphi、C++ Builder 等 Borland 编译器的桌面与数据库应用开发者,用于在程序中直接访问 MySQL、MSSQL、InterBase、Firebird、Sybase、PostgreSQL 等多种数据库,并内置正则表达式与 SQL 解析对象,可显著简化跨数据库连接、查询与组件封装工作。压缩包共 387 个文件,约 1.19MB,以 92 个 pas 源码文件为核心,辅以 64 个 res 资源、42 个 dpk 包描述以及 bpg/bpk 工程组、dpr 工程文件和配置说明文件,便于按模块深入阅读和重新编译集成。现有 267 人学习/下载。该版本为 6.0.12 最新稳定版,源码组织结构清晰,通过阅读组件单元可理解 ZDbc 驱动层、数据集封装与数据库协议解析的实现思路,还可直接复用正则表达式和 SQL 解析单元;对于需要适配自有数据库或排查连接异常的开发者,是很实用的参考蓝本。无论直接引用还是二次开发,都能节省大量底层适配与调试时间。
1. 还在为数据库驱动发愁?zeosdbo-6.0.12 源码给你一条稳定退路
接手一个七八年前留下的桌面项目时,我发现它还在用一个老旧的数据库访问层,一升级数据库版本就直接翻车。翻遍资料后,最踏实的方案反而是回头用一个持续维护的稳定版源码库,zeosdbo-6.0.12 就是我最终留下的一版。它不是一个编译好的安装包,而是整套控件源码,支持 MySQL、PostgreSQL、SQLite、Firebird、Oracle、SQL Server 等主流数据库,尤其适合 Delphi 和 Lazarus 环境。你下载下来能自己编译、自己裁剪、自己看底层实现——这就是源码资源比二进制安装包强的地方。这篇笔记会带你从编译装好,到配参数连库,再到动手改源码里最常踩坑的地方。
2. 把 zeosdbo-6.0.12 源码变成可用的控件:编译环境与三处关键路径
ZeosDBO 的源码包看起来目录很多,但其实入口很清晰:packages文件夹下按 IDE 版本拆好了各个包工程文件,src文件夹里是核心代码,test文件夹可以当示例工程用。你要做的不是把整个目录塞进 IDE,而是找到匹配当前 IDE 版本的包文件,按顺序编译安装。
2.1 版本对应关系:先看 IDE 再选包工程文件
源码里的packages目录一般按编译器和 IDE 版本命名,比如Delphi12、Delphi11、Lazarus这些子目录。我常用的是 Delphi 12,所以在packages\Delphi12\下会看到一堆.dpk文件,这些就是包工程,其中一个叫ZeosDBCore_D12.dpk,是核心运行时包,其余的是设计时包。如果你用的是 Lazarus,目录结构类似,但入口是.lpk。
实际编译前先确认你的 IDE 版本是否在源码的兼容列表里。ZeosDBO 6.0.12 对现代版本支持得比较全,如果用的是很新的变体,编译时可能提示某个单元找不到,那并不一定是源码问题,很可能是你选错了子目录。我的做法是先看packages目录里哪个文件夹最靠前,再直接打开对应.dproj或.dpk。
# 在源码根目录下查看各 IDE 版本的包工程文件 find packages -type f \( -name "*.dpk" -o -name "*.lpk" -o -name "*.dproj" \) | sort这个命令会把所有包工程列出来。通过路径名称能快速定位当前 IDE 对应的目录,比如packages/Delphi12/ZeosDBCore_D12.dproj。我一般还会用grep确认包里的版本号信息,避免下载的源码被手动改过。
# 检查版本声明文件,确认当前源码版本号 grep -r "6.0.12" src/ | head -5如果这里没有输出,那说明你手里的源码不是真正的 6.0.12,要么是目录改名,要么是下载源有问题。版本号在源码里通常出现在src/core/ZGlobal.pas或类似文件中,这个动作我在每次解压后都强制做一次。
2.2 编译安装操作:从源码目录到 IDE 组件栏
不同版本 IDE 的安装路径不同,但通用流程是一样的:打开包工程文件,设置好库路径,编译,再安装。下面是我在 Delphi 环境里的操作顺序。
打开packages\Delphi12\ZeosDBCore_D12.dproj,先把输出目录改到源码包内的output文件夹,防止.dcu文件散落到全盘。然后在 IDE 的 Library 路径中把src\core、src\dbc、src\components三个目录都加进去。这三个路径缺一个,编译时就会报“找不到某个 .pas 文件”。
编译顺序也有讲究:先编译运行时期包(ZeosDBCore),再编译设计时包(ZeosDBComponents)。如果先把设计时包放到 IDE 组件栏,再编译核心包,大概率会遇到循环依赖或者找不到已注册组件的报错。
# 用命令行快速验证编译环境是否就绪(Windows 下可在 IDE 终端里执行) where python where msbuild为什么查这两个?ZeosDBO 的源码里带了一些生成工具,比如写 JSON 配置或转换测试数据时可能会用到 Python。如果你的机器上没有 Python,某些测试脚本会跳过,不影响控件编译。msbuild 用于命令行编译工程,如果你偏好命令行而不是 IDE 点点点,提前确认 msbuild 可用能省很多时间。
编译完成后回到 IDE 组件栏,如果看到了ZeosAccess、ZeosConnection、ZeosDataset等组件页,说明安装成功。此时打开一个空白应用,往窗体上拖一个TZConnection,检查属性编辑器里有没有Driver下拉列表——这个下拉列表是从源码里的描述文件生成的,如果值是空的,说明设计时包没有正确注册。
2.3 验证安装是否成功:一个最小的连接冒烟测试
安装成功不等于能连接数据库,所以我会先用一个最轻量的冒烟测试:新建一个控制台项目,只引入 ZeosDBO 的核心单元,手动创建连接对象并尝试连接 SQLite。为什么选 SQLite?因为它不需要安装服务端,动态库就一两个文件,最容易排查问题。
program SmokeTest; {$APPTYPE CONSOLE} uses SysUtils, ZConnection; var Conn: TZConnection; begin Conn := TZConnection.Create(nil); try Conn.Driver := 'sqlite-3'; Conn.LibraryLocation := 'sqlite3.dll'; Conn.Database := 'smoke_test.db'; Conn.Connect; if Conn.Connected then Writeln('connect ok') else Writeln('connect failed'); finally Conn.Free; end; end.这段代码里,Driver指定为sqlite-3,对应 ZeosDBO 内部的 SQLite 驱动;LibraryLocation指向动态库所在位置;Database是数据库文件路径。注意Connect方法不会抛异常,但Connected属性会反映真实结果。
冒烟测试通过后,再回到 IDE 里做实际连接。这一步能区分开「组件没安装好」和「参数没写对」两类问题。我见过不少项目把大量时间浪费在排查网络问题上,最后发现只是动态库路径写错。
3. TZConnection 参数逐个吃透:从建连到执行 SQL 的完整路径
如果你已经成功把 TZConnection 拖到窗体上,下一步就是理解它这几十个属性分别管什么。很多人连接 MySQL 时只填HostName、Database、User、Password就点连接,连接失败后又一头雾水。其实 ZeosDBO 的驱动参数分两层:通用属性和驱动专用参数。
3.1 连接参数的设置顺序与默认值
我一般遵循这样的赋值顺序:先指定Driver,再设置LibraryLocation,最后设置HostName、Port、Database、User、Password。原因是Driver决定了后续参数如何被解释,比如Driver设为mysql-5,那么HostName会被解析为 TCP 地址;如果Driver设为sqlite-3,那么HostName就不该填,应该用Database填文件路径。
LibraryLocation这个属性很多人忽略。默认情况下 ZeosDBO 会在系统 PATH 中查找动态库,如果找不到,连接时不会立刻报错,而是在真正打开连接时才抛异常。所以我的习惯是:在开发环境里永远显式指定LibraryLocation,避免依赖系统 PATH。
Port属性在多数数据库下有默认值,比如 MySQL 默认 3306、PostgreSQL 默认 5432。如果你本机改过端口,才需要显式指定。给Database赋值时要注意:MySQL 里填的是库名,SQLite 里填的是文件路径,Oracle 里填的是服务名或 SID。同样的属性名,不同驱动语义完全不同。
3.2 代码实战:动态创建 TZConnection 并查询数据
单机数据库软件里,动态创建连接可以避开设计期连接串被误改的问题。下面这段代码演示了完整的创建、配置、查询流程:
uses ZConnection, ZDataset, ZSqlProcessor; procedure TestQuery(ADriver, ALib, AHost: string); var Conn: TZConnection; Qry: TZQuery; begin Conn := TZConnection.Create(nil); try Conn.Driver := ADriver; Conn.LibraryLocation := ALib; Conn.HostName := AHost; Conn.Port := 3306; Conn.Database := 'testdb'; Conn.User := 'root'; Conn.Password := '123456'; Conn.Properties.Add('charset=utf8mb4'); Conn.Connect; Qry := TZQuery.Create(nil); try Qry.Connection := Conn; Qry.SQL.Text := 'select id, name from user where id > :minId'; Qry.ParamByName('minId').AsInteger := 100; Qry.Open; while not Qry.Eof do begin WriteLn(Format('id=%d, name=%s', [Qry.FieldByName('id').AsInteger, Qry.FieldByName('name').AsString])); Qry.Next; end; finally Qry.Free; end; finally Conn.Free; end; end;这段代码里我特意加了charset=utf8mb4,因为默认的字符集在很多数据库上是 latin1 或旧版 utf8,写中文很容易变问号。这里通过Properties.Add方式传入,比直接改TZConnection的某些属性更可靠。TZQuery特有的ParamByName用法和普通的 ADO 组件差不多,但要注意Open方法与ExecSQL方法的区别。如果是select,用Open;如果是insert/update/delete,用ExecSQL,搞混了会得到一堆异常。
3.3 参数快查表:Driver、HostName、Port、CharacterSet 到底怎么填
我把高频参数整理成一张表,日常开发时对着抄就行:
| 属性名 | MySQL | PostgreSQL | SQLite | Firebird |
|---|---|---|---|---|
| Driver | mysql-5 或 mysql-8 | postgresql-9 或 postgresql-10 | sqlite-3 | firebird-2.5 或 3.0 |
| LibraryLocation | libmysql.dll | libpq.dll | sqlite3.dll | fbclient.dll |
| HostName | 服务器 IP 或域名 | 同左 | 不填 | 同左 |
| Port | 3306 | 5432 | 0 | 3050 |
| Database | 数据库名 | 数据库名 | 文件路径 | 数据库路径 |
| User | 用户名 | 用户名 | 不填 | 用户名 |
| Password | 密码 | 密码 | 不填 | 密码 |
特别注意Driver里的数字后缀,它不是固定的。同是 MySQL,老版本客户端库用mysql-5,新版用mysql-8;如果填错,可能连接成功但查询结果行为怪。我这个表里的值对应 6.0.12 源码自带的Drivers.ini,你可以打开resources\Drivers.ini里查完整映射。
4. 多数据库支持的关键逻辑:驱动协议映射与动态库选择
ZeosDBO 能在多个数据库之间切换,靠的是内部一套协议驱动映射。当你设置Driver := 'postgresql-9'时,它会自动加载对应的驱动描述,然后定位对应的动态库。理解这套映射,是你从“能用”到“用得顺手”的分水岭。
4.1 协议名与底层动态库的对应关系
源码中的ZConnection.pas里维护了驱动注册表,每个驱动都有一个名字,对应一个TZDriverProtocol实例。驱动名一般形式是数据库类型-版本号,而版本号并不是强制要求苛刻匹配。比如mysql-5和mysql-8其实是两个驱动条目,它们的区别在于对预处理参数、位标志等的处理方式。
在lib文件夹里能看到libmysql.dll、libpq.dll、sqlite3.dll。这些动态库不是 ZeosDBO 提供的,需要你自己从对应数据库官方或第三方渠道获取。所以拿到源码包后,杀气腾腾地去找里面有没有libmysql.dll是没有结果的——这个源码包只是控件层,数据库客户端库你得另外下载。
4.2 切换数据库时应该改哪些参数
最典型的场景:同一套业务代码要从 MySQL 迁到 PostgreSQL。不考虑 SQL 语法差异的话,参数改动点如下:
// MySQL 版 Conn.Driver := 'mysql-5'; Conn.LibraryLocation := 'libmysql.dll'; Conn.Port := 3306; Conn.Properties.Add('charset=utf8mb4'); // PostgreSQL 版 Conn.Driver := 'postgresql-9'; Conn.LibraryLocation := 'libpq.dll'; Conn.Port := 5432; Conn.Properties.Add('charset=UTF8');除了这些基础参数,要注意事务隔离级别。MySQL 默认的Repeatable Read和 PostgreSQL 的默认行为不同,ZeosDBO 的TZConnection提供了TransactIsolationLevel属性,迁移时建议显式设为和原库一致。另一个容易忽略的点是AutoCommit,MySQL 的某些驱动会自动提交,PostgreSQL 不会,代码里如果看起来不 commit,迁移后数据会莫名丢失。
4.3 多数据库配置管理:用配置文件驱动连接参数
如果项目要求在多个数据库之间切换,我会做一个简单的配置系统,而不是在代码里写死。常见做法是把连接参数放进一个 ini 或 JSON 文件,程序启动时动态创建TZConnection。这样数据库切换时只需改文件,不用重新编译。
[default] driver=mysql-5 library_location=libmysql.dll hostname=127.0.0.1 port=3306 database=appdb username=root password=xxx charset=utf8mb4这段 ini 结构清晰,ZeosDBO源码里也自带TZProperties和TZINI相关的处理单元,可以直接解析。我一般自己写一个简单的函数读取这些键值,然后赋值给TZConnection。如果迁移到 PostgreSQL,只需要把driver改成postgresql-9,再改一下port和database的值。注意database这个键在不同库中含义不同,所以移植时数据文件的位置也要留意。
有了配置驱动,测试和生产环境的切换就变成了一条命令的事。我见过很多团队,连接参数分散在十几个窗体里,最后就算只是换个密码都要全量搜索替换。源码里这种集中管理的思路才是长期维护的正道。
5. 常见问题排查:编译失败、连接超时、乱码与 SQLite 报错
这一章是我实际操作中踩过最多次的坑,每一条都可以单独写一篇文章。这里挑四个高频问题,按“现象 → 原因 → 解决”的格式写,保证你能对着处理。
5.1 编译时提示找不到包单元:路径优先级问题
现象是:打开ZeosDBCore_D12.dproj后点编译,IDE 提示ZConnection.pas not found。
原因:IDE 的 Library 路径没有包含 ZeosDBO 的src\core目录。有些版本在解压源码后,并没有自动把路径加进去,需要手动设置。另一个常见原因是 IDE 里已经有旧版 ZeosDBO 的残留路径,新解压的目录被忽略。
解决:检查 IDE 的 Tools -> Options -> Library 里是否存在src\core、src\dbc、src\components这三个路径。若存在旧路径,先删掉再用新的。如果还报错,检查.dproj文件里的UnitSearchPath是否相对路径,相对路径可能因为源码包目录被移动而失效。
5.2 MySQL/PostgreSQL 连接超时:动态库版本不匹配
现象是:代码里连 MySQL,Connect方法被调用后等了十几秒,然后报Can't connect to MySQL server。
原因:library_location指向的libmysql.dll是 32 位的,而你的程序是 64 位;或者你装的是新版 MySQL 客户端库,协议版本与 ZeosDBO 驱动不兼容。ZeosDBO 的mysql-5驱动可能无法匹配 MySQL 8 的认证协议。
解决:把动态库换成和应用程序位数一致的版本,比如 64 位程序用libmysql.dll64 位。如果还是超时,把驱动改为mysql-8再试。对 PostgreSQL 来说,libpq.dll版本太新或太旧都会导致类似问题。我一般会先建一个最小测试程序,只做连接操作,通过逐步切换动态库版本定位问题。
5.3 中文乱码:CharacterSet 与数据库编码要一起调
现象是:插入的中文正常,但读出来是问号或乱码;反过来写进去就乱。
原因:客户端和服务器端的多字节字符集没有对齐。MySQL 数据库本身是 utf8mb4,但 ZeosDBO 的Properties.Add('charset=utf8mb4')没设置,或者连接的字符集被默认成 latin1。另外,数据库表里如果字段定义的是latin1,你怎么设置客户端都救不了。
解决:先确认服务器端字符集,然后在代码里把charset设成一致的。比如 MySQL 用utf8mb4,PostgreSQL 用UTF8。这里有个细节:charset是动态库和服务器协商的字符集,而不是你的编辑窗口代码用的字符集。如果程序源码本身是 ANSI 编码,那么代码里的中文字符串也会出问题,建议源码统一用 UTF-8。
5.4 SQLite 打开失败:动态库路径与文件权限的前后顺序
现象是:用sqlite-3驱动打开一个已有的.db文件,ZeosDBO 报unable to open database file。
原因:表面上像是文件权限不足,但很多时候是library_location指向的目录没有写权限,或者数据库文件所在目录不存在。还有一种情况是,你给Database属性传了相对路径,但运行时当前目录不是你预期的那个目录。
解决:我一般会先用系统自带工具或者命令行sqlite3.exe测试一次该文件能否打开,排除文件本身损坏的可能。然后,代码里把Database改成绝对路径,并把LibraryLocation也改成绝对路径。最后检查目标目录的写权限——SQLite 在打开连接时可能需要创建临时文件。如果还不行,用Process Monitor一类工具看进程尝试打开了哪些路径,这一步往往能直接看到问题。
6. 从源码里挖出更多价值:按需裁剪组件和快速定位异常语句
当你能在一套源码上稳定编译、连接、查询之后,就可以考虑怎么把源码的价值用满。这里分享两个我很常用的技巧,以及一个已经养成习惯的验证流程。
6.1 裁剪不需要的驱动,减小 BPL 体积
如果你的产品只面向一个数据库,却把 ZeosDBO 默认带上的一大堆驱动都编译进去,BPL 或 EXE 体积会白白大好几倍。源码里每个驱动通常对应一个单元,文件在src\dbc目录下,比如ZDbcMySql.pas、ZDbcPostgreSql.pas、ZDbcSQLite.pas等。如果确定不用 Oracle,把该驱动单元从项目文件的uses列表里移除,再重新编译。注意这样做之后,TZConnection的Driver下拉列表里会少掉 Oracle,但如果你只在代码里设置驱动名而不依赖设计期下拉列表,就没有问题。
从我的经验看,裁剪掉的驱动单元并不是简单地改一行uses,你还需要删除src\dbc下的对应.dfm或.res文件引用,然后在包的 Registry 单元里移除它。源码里有一个ZDriverReg.pas,统一管理驱动注册信息。
6.2 通过源码跟踪错误堆栈,定位失败 SQL
ZeosDBO 执行失败时,异常信息有时候很笼统,比如SQLState: HY000。这时候打开源码里的ZDbcStatement.pas,找到ExecuteQuery或ExecuteUpdate方法,跟踪哪里抛了异常。我用得最多的,是给Prepare方法和Execute方法加日志输出,把实际发送给数据库的 SQL 字符串和参数值打出来。
// 在 ZDbcStatement.pas 中临时插入日志 procedure DebugSQL(Value: string); begin // 生产环境建议用 OutputDebugString,开发时可以直接 WriteLn OutputDebugString(PChar('[ZeosSQL] ' + Value)); end;这个方法能让每个 SQL 原形毕露。特别是用参数化查询时,参数名映射出错依赖一定的玄学,不看源码很难定位。我在追查一个慢查询时,就靠这个办法找到了一个少了引号的动态 SQL 拼接错误——而那是在源码判断条件下生成的语句。
6.3 我的强制走查习惯
由于经常在多个数据库项目里切换,我从那以后每接一个项目都强制走一遍固定的流程:先编译核心包,再跑冒烟测试,然后开配置驱动文件连真实库,最后用DebugSQL输出前三条关键 SQL。之前遇到过一次 SQL 查询正常但数据不匹配的问题,结果就是字符集配置漏了一条。从那以后每次我都会把charset写进配置,而不是默认值。这个习惯帮我省下了不少远程调试的时间。希望帮到你。
本文还有配套的精品资源,点击获取