简介:Absolute Database v7.93 源代码包面向使用 Delphi 4 至 11 的开发者与数据库组件研究者,提供一套可研读、编译和定制的面向对象关系型数据库引擎实现,用于理解事务处理、缓存管理、权限加密、备份恢复及多用户并发等核心机制。压缩包共 756 个文件,约 18.9MB,包含 sql 脚本、hpp/c/pas 源文件、dcu 编译单元、res/dfm 窗体资源、dpk/bdsproj 工程文件以及 chm/txt 文档,覆盖源码、工程配置、示例与说明,便于按模块检索。已有 446 人学习,说明其在 Delphi 数据库开发社区中具有一定参考价值。读者可据此追踪引擎内部结构,结合 Delphi 11 Alexandria 的组件集成方式做性能分析与二次开发,也可参考其数据库迁移、并发访问和多平台兼容设计,为维护旧项目或构建定制数据库方案提供可复用的代码基础。
1. 先选对目录:Absolute_Database_v7.93_sources_for_D4-11 不是一套包通吃
上个月帮人远程看一个老项目,他在 Delphi 11 里双击了一个AbsDB.dpk,点 Install,IDE 直接弹“Cannot load package”,组件面板一片空白,回头还顺手把 IDE 的包列表搞乱了。问题不在他操作,而在于Absolute_Database_v7.93_sources_for_D4-11这套源码包横跨 Delphi 4 到 Delphi 11 十几个版本,目录里躺着的是多份工程文件,你却只能装其中一份。数据库组件这种带设计期包的东西,装错的代价不小——轻则组件面板多出两页重复项,重则每次打开 IDE 都提示包加载失败。这篇笔记写的是我自己在几个版本上装这套源码的完整路径:先认目录,再编运行期包,再装设计期包,最后拿一个真实可读写的库文件验证,适合正在给老项目补数据库层、或者想把 BDE 依赖彻底摘掉的开发者。
2. 装前准备:Delphi 版本、平台与输出目录的一次性核对
动手之前先把三件事定死,能省掉后面八成的返工。这套包是源码版,不是二进制 dcu 版,意味着所有单元都要用你本机的编译器重新生成一遍。源码版的好处也在这里:只要源码里有对应的条件编译分支,你的编译器就能编出匹配当前 RTL 的 dcu;坏处是它对路径和输出目录比二进制包敏感得多,路径一乱,IDE 就会去别的地方找同名单元,然后报一堆看不懂的错。下面三节是我每次装之前固定要走一遍的核对流程。
2.1 确认 IDE 版本号与包目录的对应关系
解压后你会看到类似packages、source(或sources)这样的顶层结构,packages下面按版本分文件夹,常见命名是D7、D2007、D2010、DXE2、DXE8、D10_2、D10_4、D11这一类。名字不统一很正常,老包跨的年代太长,作者换过好几轮命名习惯。判断该用哪个,别看文件夹名,看 IDE 自己的版本标识:Delphi 4~7 是dcc32的老世代,工程文件只有.dpk和.bpg;Delphi 2007 之后开始有.dproj和.groupproj;Delphi XE2 起才有 Win64 平台选项;Delphi 2009 起字符串变 Unicode,源码里必然靠{$IFDEF UNICODE}分叉。
对照表可以这么记:
| IDE 世代 | 工程形态 | 关键差异 |
|---|---|---|
| Delphi 4~7 | .dpk+.bpg | 无 Unicode,包只编 Win32 |
| Delphi 2005~2007 | .dpk+.dproj混用 | 开始有.dproj,仍需核对 |
| Delphi 2009~XE | .dproj | UNICODE已定义,字符串全部变了 |
| Delphi XE2~XE8 | .dproj+ 平台节点 | 出现 Win64,需要分别编包 |
| Delphi 10.x~11 | .dproj+.groupproj | 平台配置写在工程里,别手改 |
提示:拿不准就打开 IDE 的 Help > About,把版本号记下来,再去
packages下找最接近的那一层。宁可装低一档的目录试编,也别把 D7 的 dcu 硬塞给 D11。
这里最容易翻车的是“降档使用”。有人图省事拿 D2010 的目录在 XE2 里打开,编译能过,装上去组件也能出现,但一放到项目里就报Unsatisfied forward or external declaration——因为源码里的条件分支根本没覆盖到你的编译器版本,某些方法只有声明没有实现。这种错不会在装包时报,只会在你正式用的时候炸,排查成本翻倍。
2.2 规划 BPL/DCP/DCU 输出目录(三个路径别混)
Delphi 装包涉及三类产物,很多人把它们混在一个目录里,然后互相覆盖:
- BPL:运行期包和设计期包的二进制包文件,运行时被 IDE 或 exe 加载;
- DCP:包编译时生成的符号信息,供下一个包引用;
- DCU:单元编译产物,供普通工程直接引用。
源码包编译时,设计期包dcl*依赖运行期包*,编译器要通过 DCP 找到运行期包的接口。如果你把两个包的输出路径设成不一样,或者用了 IDE 默认的“与工程同目录”,那dclAbsDB编译时找不到AbsDB的 DCP,就会退回去找源码单元重新编一份,结果是同一批单元在内存里有两份,装完组件能用,但调试时断点乱跳、异常栈错位。
我一般固定这么规划:
D:\DelphiLib\AbsDB793\bpl\Win32 <- BPL 输出 D:\DelphiLib\AbsDB793\dcp\Win32 <- DCP 输出 D:\DelphiLib\AbsDB793\dcu\Win32 <- DCU 输出在 IDE 里对应 Project > Options > Delphi Compiler 下的Package output directory、DCP output directory、Unit output directory三项,两个包必须填同一套。命令行编译时对应/LE、/LN、/NU这类开关,用 msbuild 的话是/p:DCC_BplOutput、/p:DCC_DcpOutput、/p:DCC_DcuOutput。
注意:路径里不要有中文和空格,
Program Files这种带空格的路径也别用。Delphi 的包编译器对空格的处理在不同版本间不一致,老版本会截断参数。
2.3 备份与还原点:装坏了怎么退回
装包这件事,血泪经验是“先想好怎么退”。IDE 的已安装包列表存在注册表里(HKCU\Software\Embarcadero\BDS\<版本>\Known Packages这类位置),手工删容易删漏。我的做法是装之前先导一份:
# 以管理员身份打开 cmd,导出已安装包列表,出问题直接还原 reg export "HKCU\Software\Embarcadero\BDS\22.0\Known Packages" D:\backup\knownpackages_d11.reg /y这条命令把当前 IDE 认识的包清单整个存下来,22.0是 Delphi 11 的内部版本号,10.4 是21.0,10.3 是20.0,按你的实际版本改。出问题时双击.reg还原,重启 IDE,组件面板就回到装之前的状态。
另外源码包解压后建议先整个复制一份到工作目录再动。有人直接在解压目录里编译,编完发现目录被生成的 dcu、dcp 塞满,想重新来一遍都分不清哪个是原始文件。我一般保留一份_orig副本,编译只在工作副本里做。
3. 编译运行期包与安装设计期包:从 AbsDB.dpk 到 dclAbsDB
这套包的安装顺序是硬性的:先编运行期包,再装设计期包。运行期包提供组件类的实现,设计期包只负责把组件注册到 IDE 的工具面板上。顺序反了,设计期包编译时会找不到运行期包的接口,然后编译器会自作聪明地去源码目录重新编一份,你得到的是一个“假装装好了”的 IDE——组件在面板上,但拖到窗体上就报错。
3.1 先编运行期包,别急着点 Install
打开对应版本目录,找到不含dcl前缀的那个包工程,名字通常是AbsDB.dpk或带版本后缀的形式。以 Delphi 11 为例,先确认工程里Requires段引用的都是 RTL/VCL 的标准包,没有指向别的第三方包。然后执行编译:
# 在 Delphi 安装目录的 bin 下执行;路径换成你自己的工程路径 msbuild "D:\DelphiLib\AbsDB793\packages\D11\AbsDB.dproj" ^ /t:Build /p:Platform=Win32 /p:Config=Release ^ /p:DCC_BplOutput="D:\DelphiLib\AbsDB793\bpl\Win32" ^ /p:DCC_DcpOutput="D:\DelphiLib\AbsDB793\dcp\Win32" ^ /p:DCC_DcuOutput="D:\DelphiLib\AbsDB793\dcu\Win32"/t:Build是完整重建,不是增量编译,第一次装包必须用它,否则残留的旧 dcu 会让编译器跳过实际编译。/p:Platform=Win32明确平台,别指望默认值——有些工程的默认平台是空的,msbuild 会拿第一个可用平台凑数。三个输出目录参数就是上一节规划的那三个,保持一致。
命令行编不过也别慌,回到 IDE 里打开同一个.dproj,Project > Build 一次,看 Messages 窗口第一条错误。编译失败通常是源码里的条件编译没覆盖到你的版本,比如某个方法在{$IFDEF VER350}分支里才有实现。这种情况正确做法是往上找一档能编过的目录,而不是去改源码——改源码会在下次升级时全部丢失。
编完检查一下输出目录,应该有.bpl和一批.dcu,dcp目录下有.dcp。三个目录都有东西,才算这一步过关。
3.2 安装设计期包与组件面板落地
运行期包编译通过后,打开同目录下的dclAbsDB.dpk(或对应名字的设计期包),确认它的Requires里已经包含刚编好的运行期包,然后右键 Install。IDE 会先编译再注册,成功后组件面板会多出一个页签,里面是数据库、数据集、查询这几类组件。
如果 Install 之后面板没变化,去 Component > Install Packages 看列表里有没有新条目,勾选状态对不对。还有一种情况是面板出现了但组件是灰的,那基本是运行期包的 BPL 没被 IDE 加载——检查 IDE 的环境变量 PATH 里有没有包含你的bpl\Win32目录,没有就加上,重启 IDE。
64 位项目要特别注意:设计期包只需要 Win32 一份,因为 IDE 自己是 32 位进程;但运行期包要按你的目标平台分别编。如果项目要出 Win64,就在同一个工程上切平台再 Build 一次,输出到bpl\Win64,然后确认库路径里 64 位那一份指向正确。
3.3 库路径(Library Path / Browsing Path)怎么填
这一步是最容易留下隐患的地方。很多人为了“方便”,直接把源码目录加进 Library Path,结果项目编译时用的是源码单元,跟包里的 dcu 版本不一致,运行期出现莫名其妙的访问违例。
正确的填法是:
- Library Path只加
dcu\Win32(或 Win64),不加源码目录; - Browsing Path可以加源码目录,它的作用只是让 IDE 的代码跳转能找到实现,不参与编译;
- Debug DCU Path保持默认,别指到第三方目录去。
在 IDE 里对应 Tools > Options > Language > Delphi > Library,平台下拉框选 Win32 和 Win64 分别配一遍。这两个平台的路径是分开存的,很多人只配了 Win32,切到 64 位就报找不到单元。
提示:配置完路径后,把项目里所有
*.dcu、*.identcache、__history之类的中间产物清掉再编译一次。Delphi 的增量编译对路径变更不敏感,不清缓存你会以为路径没生效。
4. 在项目里配置数据库连接:TABSDatabase 参数逐个说清
包装好只是环境通了,真正落地是在项目里把一个库文件跑起来。这套组件的核心是三个东西:数据库连接、表、查询。它走的是单文件嵌入式路子,不需要装数据库服务,也不需要 BDE 或 ADO 那套驱动层,这对老项目迁移特别友好——原来靠 BDE 别名指向一堆配置,现在一个文件路径就完事。下面按我实际配的顺序写。
4.1 DatabaseFileName、Password 与只读/独占选项
连接对象上有几个属性是必须先定的。DatabaseFileName指向库文件路径,建议用相对路径配合ExtractFilePath(ParamStr(0))拼出来,这样发布时整个目录拷走就能跑:
// 连接对象的最小配置,放在窗体创建或数据模块 OnCreate 里 var DbDir: string; begin DbDir := ExtractFilePath(ParamStr(0)); // 以 exe 所在目录为基准 ABSDatabase1.DatabaseFileName := DbDir + 'data\app.abs'; ABSDatabase1.Password := ''; // 未加密库留空,加密库填实际口令 ABSDatabase1.ReadOnly := False; // 只读部署时置 True,可避免多进程写冲突 ABSDatabase1.LoginPrompt := False; // 无人值守程序必须关掉,否则弹框卡死 ABSDatabase1.Open; end;ExtractFilePath(ParamStr(0))保证在任何工作目录下启动都能找到库文件,这是踩过坑之后的固定写法——早期我用相对路径data\app.abs,从 IDE 里跑没问题,一做成快捷方式启动就报文件不存在。LoginPrompt一定要关,服务型程序或计划任务里没人给你点确定,弹框会让进程挂在那儿。ReadOnly在只读部署场景建议打开,多进程同时写同一个库文件时能少一类锁冲突。
加密相关的属性以你手里AbsDB单元的接口声明为准,版本之间枚举名有差异。常见做法是先设口令再建库,建完的库后续每次打开都要给同样的口令,口令错了报的是打开失败而不是解密失败,别被错误信息误导。
4.2 TABSTable 与 TABSQuery 的最小可用配置
连接建好之后,取数据有两条路:TABSTable适合直接绑到网格上做增删改,TABSQuery适合带条件查询。两者都通过Database属性挂到连接对象上。
// 表对象:绑定连接、指定表名,打开即可编辑 ABSTable1.Database := ABSDatabase1; ABSTable1.TableName := 'customer'; ABSTable1.Open; // 查询对象:SQL 走 TStrings,参数用 Params 传值 ABSQuery1.Database := ABSDatabase1; ABSQuery1.SQL.Clear; ABSQuery1.SQL.Add('select id, name, amount from customer where amount > :minAmount'); ABSQuery1.Params.ParamByName('minAmount').AsFloat := 1000; ABSQuery1.Open;Database属性是必须显式赋的,即使在数据模块里只放了一个连接对象。有人指望“自动找同名的连接”,在这套组件上不成立,漏赋就是打开时报“未指定数据库”。
参数化查询别用字符串拼接,Params传值既避免引号转义问题,也省得被单引号里带特殊字符的数据坑到。Open和ExecSQL的区别要分清:select走Open,insert/update/delete走ExecSQL,用错了前者不返回结果、后者不产生数据集,都不报错但结果不对。
4.3 免 BDE 发布与运行期自动建库
源码版的最大价值在这:整个数据库引擎编进 exe,发布时不需要额外注册任何组件、不需要在客户机上装 BDE、不需要配别名。我在一个老系统迁移里就是靠这个把部署步骤从“装 BDE + 配别名 + 注册驱动”压到“拷一个目录”。
首次运行自动建库也是常见需求,写法上就是先判断文件存在,再建库建表:
// 首次启动时自动建库并建表,避免手工准备空库 if not FileExists(ABSDatabase1.DatabaseFileName) then begin ForceDirectories(ExtractFileDir(ABSDatabase1.DatabaseFileName)); ABSDatabase1.CreateDatabase; // 建空库,具体方法名以 AbsDB 单元为准 ABSDatabase1.Open; ABSDatabase1.ExecSQL('create table customer (id integer, name varchar(64), amount numeric(12,2))'); end else ABSDatabase1.Open;ForceDirectories那行别省,客户机上data目录通常不存在,直接建库会失败。CreateDatabase这类建库入口在不同版本里名字可能不同,编译一下就知道,别照抄。建表语句尽量用最保守的类型,varchar、integer、numeric这几个在跨版本时兼容性最好,日期类型各版本处理有差异,能存字符串就别硬用日期类型。
注意:库文件被一个进程打开时,另一个进程的写操作可能失败。多实例部署时要么加锁机制,要么在
ReadOnly模式下分发只读副本。
5. 避坑与排查:Absolute Database v7.93 装完不能用的 5 个现场
下面五条是我在不同机器、不同 Delphi 版本上真实撞到过的,每条都按现象、原因、解决写。装包失败的时候先对照这套清单看一遍,能省掉大量试错。
5.1 安装设计期包报找不到运行期包
现象:右键dclAbsDB点 Install,编译输出里第一行就是找不到AbsDB相关单元或包,或者提示某个包“must be recompiled”。
原因:运行期包没编,或者编了但输出目录和设计期包期望的 DCP 路径不一致。Delphi 找包依赖只认 DCP 路径,不认源码路径。
解决:回到运行期包工程,确认三个输出目录和设计期包填的完全一致,Build 一次,确认dcp目录下有对应文件,再回去 Install。如果还是不行,检查设计期包的Requires里那一行写的包名,和你实际编出来的.bpl文件名是否一致——大小写不敏感但拼写必须对。
5.2 组件面板出现重复页签或组件变灰
现象:装完发现工具面板里同一个组件出现两遍,或者页签在但组件图标是灰的,拖不动。
原因:之前装过别的版本或别的副本,旧的 BPL 还留在 IDE 的已安装列表里。IDE 启动时会把列表里的包全加载一遍,同名包加载两次就出现重复注册。
解决:Component > Install Packages 里把所有跟这套组件相关的条目先 Remove,重启 IDE,再装一次。如果 Remove 后还复现,用 2.3 节导出的注册表备份还原,彻底清净之后再装。别手动去删 BPL 文件,删了列表项还在,下次启动还是报加载失败。
5.3 切到 64 位平台后编译报单元找不到
现象:Win32 编译一切正常,切到 Win64 就报找不到单元,或者链接期报一堆未解析符号。
原因:只编了 Win32 的运行期包,Library Path的 Win64 那一栏是空的或者还指向 Win32 的 dcu 目录。Delphi 的库路径按平台分开存,很多人只配了一遍。
解决:在 Tools > Options > Library 里把平台切到 Win64,把dcu\Win64加进去;运行期包工程切到 Win64 平台再 Build 一次,BPL 也要单独输出。设计期包不需要 64 位版本,装 Win32 那份就够 IDE 用。
5.4 项目编译时出现重复单元警告
现象:编译项目时提示某个单元“is also defined in...”,或者断点打不上、异常栈里的行号对不上。
原因:源码目录被加进了 Library Path,项目编译时优先用了源码单元自己编了一份,跟包里的 dcu 形成两份实现。IDE 调试器加载的符号和实际运行的代码不是同一份。
解决:从 Library Path 里删掉源码目录,只保留dcu输出目录;需要看源码就把源码目录加到 Browsing Path。改完清掉项目里的*.dcu和*.identcache重新编译。
5.5 升级 IDE 小版本后组件全部消失
现象:IDE 打了补丁或者小版本升级,打开项目发现组件面板上的数据库页签没了,项目里的组件变成红叉。
原因:IDE 升级会重建包缓存,旧 BPL 跟新 RTL 的二进制接口不匹配,加载失败后 IDE 直接把条目禁用了。
解决:不用重装整套,把运行期包和设计期包按 3.1、3.2 的流程重新 Build + Install 一遍就行。源码版的好处就在这里——重新编一次就对齐了,二进制版遇到这种情况只能等厂商发新包。这也是我坚持用 sources 版的原因之一。
6. 装完怎么验证:跑通一个建库、写、读、关、再开的闭环
组件装上了不等于环境能用,我见过太多“面板上有图标、拖到窗体就报错”的情况。验证别只看能不能拖组件,要走一个完整闭环:建库、建表、写一条、读回来、关掉、重新打开再读一次。最后一步最关键,很多问题只在第二次打开时才暴露。
我一般新建一个空白 VCL 工程,放一个连接对象、一个表对象,窗体上扔一个按钮,代码写成这样:
procedure TForm1.Button1Click(Sender: TObject); var DbFile: string; begin DbFile := ExtractFilePath(ParamStr(0)) + 'verify.abs'; if FileExists(DbFile) then DeleteFile(DbFile); // 每次验证从干净状态开始 ABSDatabase1.DatabaseFileName := DbFile; ABSDatabase1.LoginPrompt := False; ABSDatabase1.CreateDatabase; // 建库入口名以 AbsDB 单元为准 ABSDatabase1.Open; ABSDatabase1.ExecSQL('create table t1 (id integer, name varchar(32))'); ABSDatabase1.ExecSQL('insert into t1 values (1, ''hello'')'); ABSDatabase1.Close; // 第一次关闭,释放文件句柄 ABSDatabase1.Open; // 关键:重新打开再读 ABSTable1.Database := ABSDatabase1; ABSTable1.TableName := 't1'; ABSTable1.Open; ShowMessage(ABSTable1.FieldByName('name').AsString); // 期望输出 hello ABSTable1.Close; ABSDatabase1.Close; end;每一步的检查点是这样的:
| 验证环节 | 期望结果 | 失败通常说明 |
|---|---|---|
| CreateDatabase | 目录下出现.abs文件 | 方法名不对或目录无写权限 |
| Open 首次 | 无异常 | 口令错、路径错、BPL 版本不匹配 |
| create table | 无异常 | SQL 方言差异,换类型试 |
| insert | 无异常 | 表名拼写或字段数不匹配 |
| Close 后再 Open | 无异常 | 文件句柄没释放,多见于异常路径 |
| 第二次读取 | 拿到 hello | 打包进 exe 的单元和 BPL 不一致 |
第二次Open那步是我特意加的。早期我不加这一步,装完跑通了就交付,结果客户机上程序退出再启动报“文件被占用”,回头查了半天才发现是异常分支里没调Close,句柄留着。从那以后我每次装这类数据库组件,都强制走一遍“建库—写—读—关—再开”的循环,哪怕只是给老项目补一个连接。跑通这一圈,才算真的装好了。希望帮到你。
本文还有配套的精品资源,点击获取