简介:这套Delphi 13.1控件包面向使用Embarcadero Delphi进行数据库应用开发的工程师,提供FibPlus组件从Delphi 5到Delphi 13的完整源码、工程与文档,用于快速构建Oracle、MySQL、PostgreSQL等数据库连接、事务管理与断线重连功能,减少底层数据库访问代码的编写量。包体共435个文件,压缩后1.82MB,包含137个pas源码、68个dproj工程、55个dpk包定义、54个dfm窗体以及若干bcb项目、说明文档和SQL脚本,覆盖不同开发环境下的编译与部署需求。目前已有58人下载学习。通过该资源可获取FibPlus历代版本组件实现,便于在旧项目维护中复用,或在新项目中集成经长期验证的数据库访问层;同时,工程文件与源码结构完整,适合查阅组件内部机制、自定义扩展以及排查连接疑难问题。
1. Delphi 13.1 控件之 FibPlus:一个 7z 压缩包背后,是 Firebird 项目的兼容性出路
如果你手上有一个叫fibplus-D5-D13.7z的压缩包,多半是想在 Delphi 13.1 里继续用 FibPlus 控件。这套控件是 Firebird/InterBase 数据库的专业客户端组件,历史比 FireDAC 早得多,大量旧系统从 Delphi 5 一路写过来,代码里全是TpFIBDatabase、TpFIBQuery,换驱动等于重写业务层。FibPlus 能活到今天,靠的是源码级兼容多版本 Delphi:D5、D6、D7 一直到 D13,同一个包里有各版本 IDE 的工程文件,解压后自己编译即可安装。这篇笔记就按这个路径走:先拆包,再装包,然后连库排坑。
2. 为什么这套 D5~D13 控件包能顶替官方驱动:FibPlus 的定位与包结构
FIBPlus 不是 ORM,也不是通用数据库框架,它直接封装 Firebird/InterBase 客户端 API。这种“专精一个数据库”的定位,决定了它在处理生成器、事件、数组字段、PSQL 调用时,比通用组件更顺手,也更接近底层语义。很多老项目从 Delphi 7 迁移到新 IDE 时,官方 FireDAC 虽然功能强大,但代码迁移成本极高,而 FIBPlus 的单元名、组件名、属性名几乎不变,重编译就能跑。这个特性让它在中小型桌面系统里一直有一席之地。
2.1 选型逻辑:FIBPlus 和 FireDAC、dbExpress、Zeos 的差别
先看一张对比表,方便你判断自己该不该继续用 FibPlus。
| 维度 | FIBPlus | FireDAC | dbExpress | Zeos |
|---|---|---|---|---|
| 数据库定位 | 只做 Firebird/InterBase | 多数据库通用 | 多数据库通用(已边缘化) | 多数据库通用 |
| 老代码兼容性 | 组件名几乎不变,迁移成本低 | 需要换组件、换属性,改动大 | 单向数据集,早已不维护 | 接口与 TDataSet 兼容,但底层行为有差异 |
| Firebird 特性支持 | 生成器、事件、数组字段、PSQL 原生支持 | 部分支持,需要绕 | 不支持 | 基础支持 |
| 部署复杂度 | 只需要带 fbclient.dll 和源码编译出的 BPL | 驱动多、配置项多 | 依赖驱动配置 | 配置简单但大事务下性能一般 |
| 学习成本 | 会 TDataSet 就会用 | 需要熟悉 FireDAC 的 Connection 模型 | 低 | 低 |
从这个表能看出,如果项目跟 Firebird 深度绑定,特别是有大量生成器、事件、存储过程交互,FIBPlus 依然是省事的选择。FireDAC 当然也能连 Firebird,但你会发现自己总是在处理“通用抽象”和“数据库特性”之间的缝隙,比如事件回调、批量写入的方言差异。与其花两天改代码,不如花半天把旧控件在新 IDE 里编译一遍。
2.2 解压 fibplus-D5-D13.7z:先看目录结构,再决定用什么版本
拿到fibplus-D5-D13.7z,先用 7-Zip 解压到固定目录,比如D:\Components\FIBPlus。别解压到桌面或者临时目录,因为后面要反复给 IDE 加路径,目录一旦移动,Library Path 全部要重配。
常规的 FibPlus 包内会有这样几个目录:
| 目录/文件 | 作用 | 安装时怎么处理 |
|---|---|---|
Source或Source32 | 32 位编译用的单元源码、公共 .inc 头文件 | 加入 IDE 的 Library Path |
Source64 | 64 位编译用的 API 封装和连接库声明 | 做 Win64 目标时加入 |
Package\D5~Package\D13 | 各 Delphi 版本对应的 .dpk / .dproj 工程 | 用 IDE 打开对应版本编译安装 |
Bpl/Dcu | 预编译产物或上次编译的输出 | 不要直接拿来用,跨版本必坏 |
这里有个原则要记住:不同 Delphi 版本的编译器 ABI 不互通,预编译的 Dcu/Bpl 即使能装上,运行期也可能因为 RTL 版本不一致而报错。所以拿到包后,别去翻 Bpl 目录碰运气,直接找对应 D13 的源码工程自己编译。D13在这个包里对应的是新版本 Delphi 的工程目录,也就是你在 Delphi 13.1 里要打开的那一组。
解压后先把整个目录刷一遍杀毒,Fab 的包经常被安全软件误报 fbclient.dll 或 BPL 是风险文件。如果解压过程中被杀软隔离了控件文件,后面编译到一半会报“找不到 XXX.bpl”,特别耽误时间。下载页写了密码就先输入密码解压,解压完右键包文件 → 属性 → 底部“解除锁定”,这一步不做,IDE 打开 .dpk 时可能被 Windows 阻挡。
2.3 安装前检查:位数、杀毒与条件编译宏
Delphi 13.1 的 IDE 宿主进程是 32 位,但可以编译 Win32 和 Win64 目标。FIBPlus 包里的 D13 工程要确认它引用的到底是 Source32 还是 Source64。正常情况是:设计时包和运行时包都编译成 32 位,给 32 位 IDE 使用;你的业务项目如果要做 Win64,再单独把 Source64 里的单元编一遍。
另一个容易被忽略的点是条件编译宏。FIBPlus 源码里靠USE_FIREBIRD一类的宏区分 InterBase 和 Firebird 两种客户端 API。如果你连的是 Firebird 3/4/5,而宏没打开,编译期不报错,运行期连接时才会发现函数入口对不上,直接给你一个“Client library could not be loaded”或者干脆连不上。这个坑藏得深,等编译安装都做完了才发现会很折腾。
提示:安装前先把 IDE 或杀毒软件里对
D:\Components\FIBPlus的实时防护关掉,等编译完再开。不是每个杀软都误报,但误报一次就够你排查半天。
3. 在 Delphi 13.1 里编译安装 FibPlus:从打开 .dpk 到拖出第一个连接
这一章是整个流程最核心的部分。FibPlus 不像普通第三方控件只需要复制一个 BPL 就能用,它需要在 IDE 里打开源码包工程,先编译再安装。步骤如下:先配源码搜索路径,再编译运行时包,最后安装设计时包。哪个前置没做好,后面都会以各种报错的方式提醒你。
3.1 先在 IDE 里把源码路径放对,后面才不会到处撞错
打开 Delphi 13.1,进入Tools → Options → Environment Options → Delphi Options → Library。在Library path里把 FIBPlus 的Source目录加进去。注意加的是Source根目录,不是Package\D13,因为 .dpk 编译时需要找到公共的头文件.inc和各个单元文件。Package\D13里只有工程文件,单元都在Source里。
Library path的排序也有讲究。如果这台机器装过旧版 FIBPlus,优先检查列表里有没有指向别的 FIBPlus 目录。多个版本的 FIBPlus 源码头文件同时存在,编译器会按搜索顺序取第一个匹配的.inc,一旦取到旧版,后面全是版本宏不匹配的报错。我的习惯是把新解压的目录放在最上面,然后把旧版本的路径全部删掉。
提示:同一台机器只留一份 FIBPlus 源码。混用版本是“签名字节不一致”“找不到单元”的头号原因。
3.2 编译设计时包与运行时包:点错的代价
进入Package\D13,正常情况下会看到两类工程文件:运行时包类似FibPlusD13.dpk,设计时包类似dclFibPlusD13.dpk。编译顺序必须是先运行时包,后设计时包。
在 IDE 中打开运行时包FibPlusD13.dpk,Project Manager 里右键工程节点,选Compile。这时 Messages 窗口会刷一段编译日志。看到Build成功之后,再打开设计时包dclFibPlusD13.dpk,右键Install。设计时包装完,组件面板会自动新增一个FIBPlus页面,里面是 TpFIBDatabase、TpFIBQuery、TpFIBTransaction 等一批组件。
如果你先装设计时包,IDE 会提示“无法加载包,因为另一个包尚未编译”。这时只要把顺序反过来就行,不需要重新解压。
编译失败的常见信息集中在 Messages 窗口的第一条错误:如果报Unit not found: pFIBDatabase.pas,那是第一步 Library Path 没配置对。如果报类似E2086的版本相关错误,多半是.inc里的 Delphi 版本宏和当前 IDE 不匹配,去Source下的公共头文件里找DELPHI_VERSION相关判断,把对应 D13 的分支打开。
3.3 最小验证工程:一个 TpFIBDatabase 连上本机 Firebird
安装成功后,立刻建一个 Blank VCL 项目来验证,不要直接开老项目。拖一个TpFIBDatabase和一个TpFIBTransaction到窗体上,把 Database 的Transaction属性指向事务组件。在OnBeforeConnect事件里写连接参数,代码如下:
procedure TForm1.pFIBDatabase1BeforeConnect(Sender: TObject); begin // 避免在 IDE 里硬填连接串,运行期赋值更适合分发给客户 pFIBDatabase1.DatabaseName := 'localhost:C:\Data\demo.fdb'; pFIBDatabase1.UserName := 'SYSDBA'; pFIBDatabase1.Password := 'masterkey'; pFIBDatabase1.SQLDialect := 3; pFIBDatabase1.LibraryName := 'fbclient.dll'; end;这样做的原因是OnBeforeConnect在每次Connected := True之前触发,连接参数集中在一处,后期换数据库服务器地址只要改这一处。上面的代码里DatabaseName用的是本地连接串格式,localhost表示本机,C:\Data\demo.fdb是库文件路径。LibraryName := 'fbclient.dll'告诉控件加载哪个客户端库,如果你的 Firebird 是高版本,这里需要指定对应版本的客户端。
然后在FormShow里连接:
procedure TForm1.FormShow(Sender: TObject); begin // 故意放 FormShow 而不是 FormCreate,方便调试器捕获连库异常 pFIBDatabase1.Connected := True; end;放 FormShow 是因为 FormCreate 阶段 IDE 的异常处理还不够友好,连不上库时弹出的不是直观的对话框,而是一个 IDE 级异常中断,新手容易被吓住。连库成功后,窗体运行起来没有任何异常,说明安装步骤已经全部走通。
4. 用 FibPlus 操作 Firebird:连接参数、事务和写入优化
安装好只是第一步,真正干活是在业务代码里。FIBPlus 的日常操作围绕TpFIBDatabase、TpFIBTransaction、TpFIBQuery和TpFIBDataset四个组件展开。连接参数决定能不能连上,事务决定数据一致性,而写入方式直接关系到性能和锁冲突。
4.1 TpFIBDatabase 连接参数速查:照着填就不会连不上
常见的连接属性如下表:
| 属性 | 作用 | 常用值 | 备注 |
|---|---|---|---|
DatabaseName | 数据库名 | localhost:C:\Data\demo.fdb | 支持别名或完整路径 |
UserName/Password | 账号口令 | SYSDBA/masterkey | Firebird 默认账号,生产环境务必改 |
SQLDialect | SQL 方言版本 | 3 | 老库可能是 1 或 2,不要盲目设 3 |
LibraryName | 客户端库文件名 | fbclient.dll | 32 位程序用 32 位 dll,64 位类似 |
Charset | 连接字符集 | UTF8/WIN1252/NONE | 直接影响中文是否乱码 |
TraceFlags | 日志跟踪开关 | [] | 调试时可打开看 SQL 发送情况 |
SQLDialect是最容易搞错的参数。Dialect 1 对应 InterBase 6 之前的老库,不支持CURRENT_TIMESTAMP等 SQL:2003 语法。如果你连的是老库却把 Dialect 设成 3,时间字段比较和字符串拼接会翻车。反之,新库用 Dialect 1 会因为语法受限而报错。最稳妥的做法是先问 DBA 或查数据库的RDB$DATABASE字段确认版本,再决定。
Charset更关键。Firebird 安装时默认字符集可能是NONE,但个别库建库时指定了UTF8或WIN1252。连接字符集和数据库实际字符集不一致,写入中文就可能变成问号。到了排查乱码那一步再来改字符集,往往还要配合数据库端的CONNECT语句,所以一开始就填UTF8最省事,前提是数据库本身能接受 UTF8 编码。
4.2 从查询到提交:TpFIBQuery 与显式事务的配合
用 FIBPlus 操作数据,尽量养成显式事务的习惯。下面这段代码是一次完整查询、修改、提交过程:
// 查询:按参数取订单 qry.SQL.Text := 'SELECT ID, NAME, AMOUNT FROM ORDERS WHERE CUST_ID = :CustID'; qry.ParamByName('CustID').AsInteger := 1001; qry.Open; // 修改:把金额更新为 99.90 qry.SQL.Text := 'UPDATE ORDERS SET AMOUNT = :Amount WHERE ID = :ID'; qry.ParamByName('Amount').AsCurrency := 99.90; qry.ParamByName('ID').AsInteger := 1; qry.ExecSQL; // 提交:一个事务里干完所有事再提交 pFIBTransaction1.Commit;注意到这里没有写StartTransaction,因为 FIBPlus 在第一次执行 SQL 时会自动开启一个事务。如果你把TpFIBTransaction1连到了 Database 的默认事务属性上,这套流程是成立的。提交之后再来一个查询,事务会自动重新开启,不需要手动管。
但要小心 FIBPlus 老项目里常见的“一条 SQL 一个事务”习惯。有些代码把查询组件放在循环里,每循环一次就执行一次查询,然后提交一次。数据量小的时候没问题,到了几千条批量写入的场景,数据库端会产生大量事务记录,锁等待和日志膨胀都会来。要控制事务粒度,就把Commit移到循环外面,或者用显式事务包住一批写入。
4.3 取回生成器 ID 与批量写入:少一次查询,少一个锁
Firebird 的主键生成通常靠生成器(Sequence),老写法是先用GEN_ID取出下一个值,再拼 INSERT。这个做法在并发下容易重复,因为两个客户端可能同时取到同一个值。新写法是用INSERT ... RETURNING子句,核心代码:
qry.SQL.Text := 'INSERT INTO ORDERS (ID, NAME) ' + 'VALUES (GEN_ID(GEN_ORDERS_ID, 1), :Name) RETURNING ID'; qry.ParamByName('Name').AsString := 'test订单'; qry.Open; // 用 Open 而不是 ExecSQL,因为要取返回结果 id := qry.FieldByName('ID').AsInteger;这里必须用Open而不是ExecSQL,因为RETURNING会返回一行结果。如果你用ExecSQL,返回值不会进入结果集,FieldByName('ID')会报找不到字段。这个写法要求 Firebird 2.1 以上和 Dialect 3,适用范围其实挺广的。
批量写入时还有一个技巧:把多条 INSERT 放在一句EXECUTE BLOCK里。FIBPlus 能直接执行这段 PSQL,一次往返完成多条插入,网络开销小很多。但EXECUTE BLOCK内不能使用参数,数量不固定时就得循环拼 SQL,注意别让字符串拼接产生注入风险。
5. Delphi 13.1 下 FibPlus 的 5 个踩坑常见问题和排查记录
从安装到运行,FIBPlus 的坑集中在版本混用、位数不匹配、字符集和事务这四类。下面按现象描述、原因分析、解决办法三步来写,都是实操里真实撞过的墙。
5.1 组件面板找不到 FIBPlus 页:多半是安装了旧的 BPL
- 现象:设计时包安装成功提示了,但打开窗体后组件面板里找不到
FIBPlus页,或者页里组件是灰的。 - 原因:最常见的是这台机器装过老版本 FIBPlus,
Component → Install Packages里旧包还在,新装的 BPL 被 IDE 忽略了。另一种情况是旧 BPL 残留在SysWOW64或 Windows 系统目录,新包装完后被系统目录里的旧文件干扰。 - 解决:先到
Component → Install Packages里把所有 FIBPlus 相关包卸载,再到 IDE 安装目录下的Bpl里删除残留的dclFibPlus*.bpl。清理干净后重新打开 D13 的设计时包,再 Install 一次。装完后如果还是看不到,手动点Install Packages右下角的Add,把新编译出来的dclFibPlusD13.bpl加进去。
这个坑本质上是“系统上存在两份包”,编译环境指认了其中一份,IDE 又加载了另一份。清理旧包比重装新包更重要。
5.2 编译报错 Unit not found: pFIBDatabase.pas:版本混用的黑匣子
- 现象:新建项目后,在单元里
uses了 FIBPlus 的单元,编译时提示找不到pFIBDatabase.pas或类似单元。 - 原因:全局 Library Path 没加对,或者加的顺序不对。注意一个隐藏问题:Library Path 加的是 Source 根目录,但
Source下面还分多层子目录,某些包需要把子目录也加进去,比如Source\Source32。编译器只在 Library Path 指定的目录里找单元,子目录不会自动递归。 - 解决:回到
Tools → Options → Delphi Options → Library,把Source根目录和必要的子目录都加进 Library Path,目录之间用分号分隔。加完点Save,关掉 IDE 重新打开项目,让路径配置彻底加载。如果还报同样的错,搜索整个Source目录确认真的有pFIBDatabase.pas,没有就说明包不完整,需要换一份完整的源码包。
一个更隐蔽的变体是编译老项目时,项目自身库路径里也配了一份 FIBPlus 旧路径,IDE 会优先使用项目级路径。这时候要在Project → Options → Delphi Compiler → Search Path里把新路径加进去,否则全局 Library Path 根本不生效。
5.3 Win64 目标编译失败:fbclient.dll 位数不对
- 现象:32 位项目一切正常,切换 Win64 目标平台后编译报错,提示无法解析外部符号或加载
fbclient.dll失败。 - 原因:FIBPlus 的 64 位支持依赖独立的 Source64 目录。如果工程没有把 Source64 加到搜索路径,编译器沿用的还是 32 位单元声明。另一个原因是运行时找不到 64 位
fbclient.dll:64 位程序不能加载 32 位客户端库,Windows 64 位系统下 System32 里的是 64 位版本,SysWOW64 里是 32 位版本,放错位置会直接报加载失败。 - 解决:在项目搜索路径里把 FIBPlus 的
Source64目录加进去,删掉原来的 Source32 路径。然后把程序要用的 64 位fbclient.dll放到 exe 同目录下,并确认LibraryName不指向绝对路径。更保险的做法是在 FormCreate 里用pFIBDatabase1.LibraryName := 'fbclient.dll',运行时按当前进程位数加载。
5.4 中文乱码:Charset 不是摆设
- 现象:查询出来的中文显示成乱码,写入的中文再从库里读出来也是问号。有时候同一段代码在 A 机器正常,在 B 机器就乱码。
- 原因:Firebird 的字符集不是“数据库里存成什么就是什么”,连接字符集与数据库字符集不一致,客户端 API 会做转换,转换失败就成了问号。常见错误是连接属性里
Charset不填,默认NONE表示不转换,数据如何进库就如何出库。 - 解决:在
OnBeforeConnect里把Charset := 'UTF8',并确认数据库本身是用 UTF8 创建的。如果数据库建库时用的是WIN1252,你把它当 UTF8 连,转换照样会出错。另外,低版本的fbclient.dll对 UTF8 支持有缺陷,建议把客户端库升级到与你 Firebird 服务端匹配的版本。
提示:乱码问题如果死在“改完连接字符集还乱”,去查数据库建库脚本的
DEFAULT CHARACTER SET字段,两边都得对上才会好。
5.5 写入超时与死锁:事务没提交是头号血泪经验
- 现象:单用户测试写入正常,多用户一上来就报锁等待超时,甚至
deadlock。日志里频繁出现lock conflict on no wait transaction。 - 原因:FIBPlus 的查询组件默认可能在执行
SELECT时开了一个写事务,如果代码里没有及时 Commit 或 Rollback,事务一直占着记录版本。Firebird 的多版本并发机制下,其他会话修改同一行时会等待锁释放,等待时间超过服务端配置就报超时。 - 解决:先看业务代码里每个查询后面有没有 Commit 或 Rollback。一个简便的排查办法是在事务组件的
AfterCommit事件里写一个日志,统计每次事务持续时间。事务粒度尽量小,循环内不要提交,循环外提交一次。另一个方向是把事务组件的 Wait 属性设为 True,并配置合理的 Lock Timeout,这样至少不会一条锁冲突直接让整个程序崩溃,而是等待超时后给出明确异常。
这个坑是老代码里最普遍的。很多人从 Delphi 7 时代一路带过来的写法就是“开了连接不关事务”,以前数据量小没事,现在数据一多必然爆发。
6. 连接不稳定时的最后防线:FibPlus 日志与自动重连的实战写法
数据库连接在客户现场经常遇到半夜断线、网络抖动导致连接失效的情况。给 FIBPlus 程序加一个定时重连机制,是桌面系统长期稳定运行的兜底做法。我的习惯是开一个全局定时器,每秒检查一次连接状态,断线就尝试重连,重连失败写日志。
procedure TForm1.TimerReconnectTimer(Sender: TObject); begin if pFIBDatabase1.Connected then Exit; try pFIBDatabase1.Connected := True; // 如果断线前有未完成事务,必须回滚,否则新查询会报事务状态异常 if pFIBTransaction1.InTransaction then pFIBTransaction1.Rollback; except // 这里只记录日志,不弹对话框,避免每秒钟都打扰用户 LogEvent('重连失败: ' + DateTimeToStr(Now)); end; end;这段代码的精髓在InTransaction判断后主动Rollback。很多重连机制只做Connected := True,忘了旧事务没有结束,Firebird 会拒绝在新事务开启前提交老事务,结果重连成功后下一句 SQL 还是报错。调试这类问题,我会把TraceFlags打开,把 FIBPlus 发送的每条 SQL 和事务边界打到日志文件里,能直接看到每次COMMIT和ROLLBACK的触发时机。
至于日志组件,FIBPlus 自带了一套日志机制,也可以在TpFIBDatabase的各个事件里自己接管。我的做法是在 FormCreate 时指定一个日志文件路径,然后把TraceFlags里的执行计划、参数值选项打开,这样客户现场出问题,拿回来一份日志就能还原 SQL 执行顺序,少跑很多冤枉路。自动重连的定时器间隔不要低于 3 秒,不然网络抖动恢复期间会连续发起连接请求,把服务器端口占满。
用 FIBPlus 写跨十年的桌面系统,我的最大教训是:装控件不怕编译报错,怕的是同一台机器上版本混用后留下的隐性坑。换环境时先把旧包装干净、路径清干净,再上新包,能省掉一半的血泪时间。希望帮到你。
本文还有配套的精品资源,点击获取