简介:MyDAC v5.10是一套定位明确的MySQL数据访问组件源码,面向长期使用Delphi或C++Builder的桌面应用开发者,用于在不依赖中间件的情况下直连MySQL服务器,实现增删改查、事务处理、批量提交、存储过程与异步调用等常见数据库需求,并兼顾高性能和低资源占用。资源包整体仅约1.39MB,共包含1403个文件,文件类型以pas源文件、dpk与dproj工程文件、dfm窗体、res资源、bdsproj项目配置、bmp图标以及html帮助文档为主,覆盖源码、界面、工程与文档多个维度,结构清晰,适合按目录检索和二次编译。已有166人学习下载。使用者可以获得完整组件源码,还有历史更新记录、FAQ、许可协议和源码编译指引等配套说明,既能快速了解组件能力与接口用法,也能根据自身业务对数据库访问逻辑做定制化调整,适合需要在旧版Delphi和C++Builder环境中稳定接入MySQL的中高级开发者。
1. MyDAC v5.10 源文件:免装 MySQL 客户端库的 Delphi 连接层组件源码
做 Delphi 开发的人几乎都囤过一份 MySQL Data Access Components (MyDAC) v5.10 源文件。它不是装好的交付包,而是把 TMyConnection、TMyQuery、TMyScript 的整套源码摊开给你看。MyDAC 最大的价值在 Direct 模式:组件自己实现了 MySQL 网络协议,客户机不用装 libmysql.dll,部署时少带一个库。适合两类人:一类在维护 Delphi + MySQL 老系统,想把数据库连接层这个黑匣子换成自己能改的源码;另一类是研究 Delphi 组件库怎么实现 TCP 协议和结果集解析的。下面按我实际拆包的顺序,讲目录、安装、连接参数和几个踩过才知道的坑。
2. 拆包与选型:源码包里哪组包文件对应你的 Delphi 版本
2.1 目录结构:Source、Demos、Help 各负责什么
解压后先别急着双击.dpk,先把根目录看明白。MyDAC v5.10 这种源码包,目录一般是三块:Source、Demos、Help。Source里是核心单元,所有组件的.pas、.dpk、.inc都在这里,这是唯一需要参与编译的部分。有些版本的Source下还有按 Delphi 版本拆分的子目录,比如Delphi7、Delphi2009,这些子目录里往往只放包工程文件,公共代码仍在Source根目录;你编译时要用哪个子目录,取决于你手上的 IDE 版本。
Demos是官方示例工程,从简单查询到存储过程调用都有,适合在组件装完之后验证环境。但第一次打开工程不建议从 Demos 开始,因为示例工程引用的包名和路径通常和你当前 IDE 的配置不一致,直接打开会弹出一堆找不到单元的错误,反而干扰对源码包的判断。Help是编译好的.chm,里面能搜到TMyConnection每个属性的说明,很多老版本帮助文档还带源码内嵌的注释,查询字段含义比翻网页快。
判断这套资源是不是“完整源文件”,我一般只看两点:Source目录下有没有.dpk,以及有没有.inc编译条件文件。.dpk是 Delphi 包工程,负责把.pas组织成可安装组件;.inc里定义了哪些单元参与编译、哪些特性按版本开关。如果只有.pas没有.dpk,那它只是运行时单元集合,装进 IDE 组件面板需要自己手工建包工程,麻烦得多。MyDAC v5.10 这套是两者齐全的标准包,安装路径就是“先编译运行时包、再编译安装设计时包”。
2.2 版本边界:v5.10 能对上的 MySQL 与 Delphi
MyDAC 5.10 的时代大致对应 MySQL 5.x,它实现的认证协议、SQL 方言和字符集交互都停留在 5.x 的能力范围里。MySQL 5.0、5.1、5.5、5.6 我都连过,稳定工作;5.7 也碰过,没有异常。但 MySQL 8.0 默认的caching_sha2_password认证插件,在这版源码的协议握手代码里没有实现,直接用会翻车,具体处理方式放到 5.2 节讲。
Delphi 侧要看 dpk 文件名的后缀。Devart 的包文件命名习惯是带版本号的,比如MyDAC160.dpk对应用 Delphi 16(也就是 XE10),dclMyDAC160.dpk是它对应的设计时包。拿到包以后,先在Source目录里找有没有跟你 IDE 版本号一致的 dpk 文件名;找不到也不要立刻换版本,很多老包在新 IDE 里只是需要重编一次。需要注意的是,新版 Delphi 对 dcu 有严格的版本校验,老包用旧 IDE 编出的 dcu 拿到新版 IDE 里直接报F2051,这不是源码坏,是编译环境不匹配,删掉旧 dcu 重新编即可。
2.3 运行时包与设计时包:dcl 前缀决定安装顺序
一个 Delphi 组件包分两种角色,这个不弄清楚后面装了一定乱。运行时包(MyDAC*.dpk)提供的是编译期单元和运行期宿主的底层代码,你写代码时uses MyConnection用的就是它。设计时包(dclMyDAC*.dpk)是给 IDE 集成使用的,它把组件注册到窗体面板上,让你能在设计期拖动组件到 Form。设计时包含有对 IDE 开放接口的引用,因此它的编译依赖运行时包。
我见过有人只装了 dcl 包不装运行时包,结果打开 dcl 工程提示找不到MyConnection单元;也有人反过来,运行时包装好但设计时包没装,代码里能uses,但组件面板上找不到图标。正确顺序是:先编译运行时包,等它生成.dcu和.bpl之后,再编译并安装设计时包。如果你只做命令行编译、不打算在 IDE 上拖组件,那就跳过 dcl 包,运行时包足够。装完以后,组件面板里会出现一个 MyDAC 分类,里面的TMyConnection、TMyQuery就是后面要用的对象。
3. 安装进 IDE:从 Library Path 到设计时包的三段编译流程
3.1 阶段一:把 Source 目录写进 IDE 的 Library Path
Library Path 是 IDE 按顺序搜索编译单元的位置列表。默认情况下它包含$(DELPHI)\Lib等路径,IDE 编译工程时,工程自身搜索路径里找不到的.pas单元,就会来库路径里碰运气。把 MyDAC 的Source目录加进去,是为了让后续编译 dpk 时能找到MyConnection.pas这类地基单元,缺失时编译会直接报F1026 File not found。
具体操作分三步。第一步,打开 IDE 的Tools → Options → Delphi Options → Library;第二步,在Library Path输入框末尾追加<解压目录>\Source,多路径之间用分号隔开;第三步,点击Update保存。新版 Delphi 会把 32 位和 64 位的 Library Path 分开配置,如果后续要交叉编译,两个条目都要加一遍。注意不要加错到Browsing Path,那是给代码提示用的,不参与编译单元搜索,加错了编译照样找不到。
提示:如果
Source下有按版本拆分的子目录,Library Path 里就把子目录路径排在最前面,公共代码目录排后面。编译器按路径先后顺序找.pas,匹配到的第一份会生效。
3.2 阶段二:运行时包与设计时包的编译顺序
运行时包和设计时包的编译顺序错了,轻则报缺单元,重则 IDE 组件面板被污染。先把Source目录下对应你 IDE 版本的MyDAC*.dpk打开:在 Project Manager 中右键选择Compile,等待状态栏出现Compile Successful。然后打开dclMyDAC*.dpk,同样先Compile,再右键选Install。Install 完成会弹窗提示组件已注册到工具面板,这才是装完。
如果 Compile 时报F2051或E2225,说明 dcu 版本和当前 IDE 不一致,回到 2.2 的处理办法:删掉Source下所有.dcu、.bpl,然后重新编译运行时包。另外有些老版本包还会读.inc里的版本判断,如果包文件带IF条件编译,编译日志里会看到具体是哪一项被跳过了,这种不是致命错误,只是关闭了某些新功能。
安装完成的标志性验证:新建一个 VCL 工程,打开 Form 后看组件面板,如果 MyDAC 分类在且能拖出TMyConnection,环境就算打通了。不要急着写业务代码,先拖一个空组件到 Form 上,编译一次空工程确认没有缺少运行时包。这一步能过滤掉后续 70% 的“编译通过但运行报错”问题。
3.3 阶段三:工程引用与部署时的 bpl 和 dcu 选择
组件装好以后,使用方式分两种,部署取舍完全不同。第一种是动态链接运行时包:在工程Options → Packages里勾选对应 MyDAC 包,exe 编译出来后体积小,但发布时必须把MyDAC*.bpl和它依赖的 Delphi RTL bpl 一起带到目标机器。第二种是静态链接:把包文件从 Runtime Packages 里移除,单元直接编进 exe,体积增加但发布时只带一个 exe。
我自己的习惯是,对内部工具这种目标环境可控的程序用静态链接,省得丢包;对要分发到客户现场的正式系统,优先动态链接,因为后续打补丁的时候只换 bpl 文件就能更新组件层,不需要重新编译整个 exe。
部署到纯净 Windows 环境还有一个注意点:如果TMyConnection.Options.Direct设成了 False,那么组件会走官方客户端库libmysql.dll,这个 dll 必须跟着 exe 走,而且位数要和 exe 一致,32 位 exe 配 64 位 dll 会静默加载失败,连接时报“无法定位程序输入点”。Direct=True 则完全绕过这个问题,这也是我推 Direct 模式的原因。
4. 编码上手:TMyConnection 连接参数、参数化查询与事务的代码落地
4.1 属性配置:Server、Port、Direct 与 Options.Charset 的选型
TMyConnection 的连接参数核心就下面这几个,属性面板和代码里字段是同一套:
| 属性 | 典型值 | 说明 |
|---|---|---|
| Server | 127.0.0.1 | 主机名或 IP,建议写 IP |
| Port | 3306 | MySQL 服务端 TCP 端口 |
| Username | myapp | 业务专用账号,不建议用 root |
| Password | 从配置读取 | 不要硬编码到源码 |
| Database | mydb | 默认 Schema |
| Options.Direct | True | 直连协议,绕过 libmysql.dll |
| Options.Charset | utf8mb4 | 与库表排序规则保持一致 |
Server 字段我几乎不写localhost。Direct 模式是通过 TCP 协议直连服务端,localhost在部分 Windows 环境会被解析成命名管道或者 unix socket,服务端不开管道时直接连接失败,写127.0.0.1最稳。Port 默认 3306,如果现场改过端口,代码里必须显式指定,否则默认端口连上以后看着像“服务未启动”,其实是端口不对。
Options.Direct 是这组件最值得用的特性。True 时 MyDAC 自己完成 MySQL 握手和命令解析,不依赖官方客户端库;False 时它退化为客户端库封装,此时部署必须带 libmysql.dll。老项目我一般固定 True,环境干净很多。Charset 值要和数据库实际字符集对齐,MySQL 5.5 之后普遍用utf8mb4,老库如果是latin1,连接时先别强制改,否则读取可能乱码,具体在 5.3 展开。
4.2 TMyQuery 参数化查询:一段可直接落地的查询模板
下面这段是我最常用的查询写法,包含连接赋值、参数绑定、遍历结果三件事。参数绑定是 MyDAC 的一个优势点,它会在客户端把参数值转成正确的类型再发往服务端,比你手工拼字符串安全得多。
procedure QueryUsersByDept(AConn: TMyConnection; const ADept: string); var Q: TMyQuery; begin Q := TMyQuery.Create(nil); try Q.Connection := AConn; Q.SQL.Text := 'SELECT id, nickname, created_at FROM users ' + 'WHERE dept = :dept ORDER BY id DESC'; // MyDAC 的命名参数用冒号开头,ParamByName 按名称取对象 Q.ParamByName('dept').AsString := ADept; Q.Open; // Open 用于 SELECT,返回可遍历的结果集 while not Q.Eof do begin // 处理当前行数据 LogMessage(Q.FieldByName('nickname').AsString); Q.Next; // 游标后移 end; finally Q.Free; // 查询对象记得释放,否则长循环里会累积句柄 end; end;逻辑上分四步:创建 TMyQuery、绑定 TMyConnection、设置 SQL 和参数、Open 后遍历。Q.Open对应 SELECT,会获取结果集;如果执行 UPDATE/INSERT/DELETE,要换用ExecSQL,原型是ExecSQL(const SQL: string)或者带上参数数组的重载版本。ParamByName('dept').AsString := ADept这行是核心,参数类型跟随赋值的类型走,服务端收到的是字符串而不是拼接的 SQL,能有效防注入。
迭代结果集时,FieldByName('nickname').AsString每次取字段值,字段名写错会运行时报EDatabaseError,最好的规避方式是先把 SELECT 字段列表和后面取值的名字对一遍。游标Q.Next别忘了写,漏掉就是死循环。最后Q.Free释放对象,组件被反复创建的循环场景下尤其重要,Delphi 的泄漏检测打开以后能看到这类问题。
4.3 事务边界与 TMyScript 批量执行
老手写事务最怕的不是 SQL 错,而是想着事务却不知道怎么定义边界。MyDAC 的事务控制在 TMyConnection 里,StartTransaction 开启,Commit 提交,Rollback 回滚。下面是一个典型的账户扣减和增加场景,两个 UPDATE 必须在同一事务里,否则中间断电会出现钱少了但对方没收到的情况。
procedure TransferBalance(AConn: TMyConnection; AFromId, AToId: Integer; AAmount: Currency); begin AConn.StartTransaction; try // ExecSQL 执行 UPDATE,参数数组按 :amt/:id 顺序传入 AConn.ExecSQL( 'UPDATE accounts SET balance = balance - :amt WHERE id = :id', [AAmount, AFromId] ); AConn.ExecSQL( 'UPDATE accounts SET balance = balance + :amt WHERE id = :id', [AAmount, AToId] ); AConn.Commit; except AConn.Rollback; raise; // 回滚后继续向上抛异常,交给上层统一处理 end; end;ExecSQL 的重载版本第二个参数是array of const,[:amt, :id]这种写法要求占位符出现顺序和数组顺序一致,参数名本身不重要,顺序必须对。StartTransaction 之后的异常块里一定要 Rollback,否则事务停留在未决状态,后续连接复用时会报“Commands out of sync”。
TMyScript 组件要单独说一下,它是给一个脚本文件或字符串执行多条 SQL 用的,比如初始化建表、数据迁移。TMyQuery 只能一次执行一条语句,把多条 SQL 塞进 TMyQuery 的 SQL.Text 里只会执行第一条,剩下全部被忽略。TMyScript 会按分号分割语句逐条发送,适合跑建表脚本。
procedure RunInitScript(AConn: TMyConnection; const AScript: string); var MyScript: TMyScript; begin MyScript := TMyScript.Create(nil); try MyScript.Connection := AConn; MyScript.SQL.Text := AScript; MyScript.Execute; finally MyScript.Free; end; end;业务里如果需要调用 MySQL 存储过程,MyDAC 提供了 TMyStoredProc,设置StoredProcName := 'sp_reduce_inventory'然后ExecProc,输入输出参数用ParamByName访问即可。存储过程返回结果集时,ExecProc 之后遍历方式与 TMyQuery 一致,只是 Open 不能调用,改用Execute再取数据。
5. 避坑记录:MyDAC v5.10 安装与连接时五个高频报错的排查方法
5.1 编译报 F2051:单元版本不一致
现象:编译dclMyDAC*.dpk时,IDE 报F2051 Unit MyConnection was compiled with a different version of System.Types,后面经常跟一串编译版本号。这属于 Fatal Error,整个安装流程直接中断。
原因:Source 目录里残留了其他 IDE 版本生成的.dcu文件。Delphi 对 dcu 头信息里有严格的编译器和 RTL 版本校验,本机 IDE 的System.Types版本与旧 dcu 不匹配时,直接拒绝链接。另外一个常见诱因是 64 位/32 位平台的 dcu 混放在同一个目录。
解决:全盘搜索Source目录下所有.dcu和.bpl,删除干净,保留.pas、.dpk、.inc不动,然后重新编译运行时包。如果删完仍报错,检查Tools → Options → Library里的BPL Search Path,确认没有指向其他版本 MyDAC 安装目录的路径。这一步做完,F2051 九成以上能消除。
5.2 连接 MySQL 8 报 Authentication plugin 错误
现象:程序连接 MySQL 8.0.x 时报Authentication plugin 'caching_sha2_password' cannot be loaded或Unknown authentication plugin,连接被服务端拒绝,但用命令行却能连上。
原因:MySQL 8 默认创建用户时认证插件是caching_sha2_password,MyDAC v5.10 的握手协议实现不包含这个插件,它在服务端校验阶段就把客户端判定为不兼容。这属于协议层不支持,单改客户端参数解决不了。
解决:在 MySQL 服务端把该用户切换回mysql_native_password,这是 MyDAC v5.10 能正常识别的认证方式。执行下面的 SQL 后重新连接:
ALTER USER 'myapp'@'%' IDENTIFIED WITH mysql_native_password BY 'password'; FLUSH PRIVILEGES;注意'%'要换成实际的 host 范围,FLUSH PRIVILEGES只刷新权限缓存,对已存在的连接不生效,需要重连。如果你是新接触 MySQL 8 又有选择余地,更彻底的方案是升级到支持新版认证的 MyDAC 版本,但老项目里往往不便动组件,改用户认证插件是最快的后悔药。
5.3 中文乱码:连接字符集与库表 collation 不匹配
现象:查询结果里中文字符显示为问号,写入后字段内容变成乱码。明明表结构已经设置了DEFAULT CHARSET=utf8,仍然乱。这算是 MyDAC 旧版本现场最出名的血泪经验。
原因:表字符集只决定数据存储格式,连接字符集决定服务端与客户端之间传输的编码。MyDAC 的Options.Charset如果没设置,默认可能按latin1处理交互,服务端把 utf8 字段返回后按 latin1 解析,必然乱码。
解决:三个位置要拉齐。第一,数据库表结构确认是 utf8mb4;第二,连接组件设置Options.Charset := 'utf8mb4';第三,考虑到老 MySQL 5.0/5.1 对 utf8mb4 支持不完整,可以在连接建立后强制执行一次SET NAMES,确保会话级字符集不被服务端全局变量覆盖。
procedure TDataModuleMy.MyConnectionAfterConnect(Sender: TObject); begin TMyConnection(Sender).ExecSQL('SET NAMES utf8mb4 COLLATE utf8mb4_general_ci'); end;字符集问题排查时要按链路逐步隔离:先用命令行客户端查询同一张表确认数据本身不是乱码,再测组件直连不带SET NAMES,最后加上Options.Charset对比。MySQL 5.7 以下如果遇到表情符号写入异常,多半是 utf8 不够用,必须走 utf8mb4。
5.4 Direct 模式连 localhost 失败但连 127.0.0.1 正常
现象:Server := 'localhost'时连接报Can't connect to MySQL server on 'localhost' (10061),改成Server := '127.0.0.1'后立刻正常。
原因:Direct 模式是纯 TCP 连接,localhost在部分系统上会被解析为 IPv6 的::1,而 MySQL 服务端配置可能只监听了 IPv4 的 3306,导致连接被拒绝。还有一种情况是 Windows 把 localhost 解析成本机命名管道,Direct 模式不处理管道协议,于是报错。
解决:统一把 Server 写成127.0.0.1,这是最干净的方案。如果现场必须写主机名,检查 MySQL 的bind-address配置,确认服务端监听地址包含对应 IP。服务端如果开启了skip-networking,TCP 端口会完全关闭,Direct 模式必然连不上,需要把该参数设为 0 并重启 MySQL,才能接受网络连接。
5.5 连接池开启后服务端连接数被打满
现象:程序运行一段时间后 MySQL 的SHOW PROCESSLIST里出现大量Sleep状态的连接,超过max_connections,新增业务全部报Too many connections。
原因:MyDAC 的连接池在Options.Pooling := True时会保持多个物理连接复用,进程退出前如果没有显式断开,连接会残留。池里的空闲连接超时行为由服务端wait_timeout控制,客户端感知不到服务端已经断掉,下一次拿旧连接执行语句时报MySQL server has gone away。
解决:程序主窗体关闭或服务停止时,显式调用MyConnection.Disconnect释放连接池,不要只依赖对象析构。池参数也要跟着服务端限制走:Options.Pooling保持 False 是最保守的做法;打开池时注意Options.MaxPoolSize,如果服务端max_connections是 200,池上限设在 50 以下更稳妥。清理现场残留连接用:
SHOW PROCESSLIST; SELECT COUNT(*) FROM information_schema.processlist WHERE Command = 'Sleep';定位到残留连接后执行KILL 连接ID;即可。连接池化解了反复握手开销,但池的参数必须和 MySQL 服务端配置联动,任何一边单独改都会出问题。
6. 进阶验证:用连接日志和最小自检把连接稳定性查到根上
6.1 打开 OnLog 与 LogFile 观察协议握手细节
连接失败时只说“连接失败”不能说明问题。MyDAC 提供了LogFile属性和OnLog事件,把协议层交互过程落盘。设置LogFile指向一个文本文件,组件会自动把握手、认证、执行语句等环节写入;挂上OnLog事件,就能把日志同时输出到界面上:
procedure TDataModuleMy.MyConnectionLog(Sender: TObject; Event: TLogEvent; const Msg: string); begin MemoLog.Lines.Add(Format('[%s] %s', [EventToStr(Event), Msg])); end; MyConnection.LogFile := 'C:\logs\mydac.log'; MyConnection.OnLog := MyLog;TLogEvent是 MyDAC 在源码中定义的枚举,区分leConnect、leExec、leFetch、leDisconnect、leError等事件。排查连接失败时,先看有没有leConnect事件,没有说明 TCP 都没通;有leConnect但随后leError报认证插件,那就是 5.2 的问题。日志能告诉你失败发生在协议的哪一层,省得反复猜。
6.2 连接池参数:Pooling、MaxPoolSize 与等待时间
前文 5.5 提过连接池,这里补充参数调优的习惯做法。Options.Pooling设为 True 后,注意三个值:MaxPoolSize控制池内物理连接上限,默认 0 表示不限制,对服务端不友好的系统要按max_connections的 1/4 来设;PoolTimeout控制空闲连接回收时间,取值要比服务端wait_timeout小,否则客户端复用已被服务端断开的连接会报错。连接池不是越大越好,我一般在并发峰值估算的基础上加 20% 余量,宁可排队也不让服务端连接数爆掉。
6.3 一个我坚持了很多年的自检习惯
每次拿到新环境、换 MySQL 服务端或升级 IDE,我都不直接跑业务代码,而是先编译一个只有 TMyConnection 和 TMyQuery 的最小控制台程序,做三件事:连接、执行SELECT 1、开启一个事务后回滚。连接成功确认网络和端口通;SELECT 1确认协议握手和认证通过;事务回滚确认服务端支持事务且没有会话级干扰。三件事全过,我再把业务模块引进来。从那以后每次现场出问题,我都会先强制走一遍这个最小自检,把环境问题和代码问题切分开,这习惯帮我省下过大量排查时间。希望帮到你。
本文还有配套的精品资源,点击获取