简介:这份资源是 MyDAC v5.00.1.7 的完整源码包,面向使用 Delphi、C++Builder 或 Kylix 开发 MySQL 数据库应用的工程师。MyDAC 组件库支持直连 MySQL 服务器或通过客户端库通信,可作为标准 MySQL 连接方案与 BDE 的高效替代,帮助开发者写出更简洁、更快速的数据库程序。压缩包共 1297 个文件,约 2.1MB,以 348 个 pas 源码、134 个 dpk 包定义、128 个 res 资源、105 个 dfm 窗体、84 个 bdsproj 工程及 83 个 cfg 配置为主,另含 cpp、dpr、bpk、inc、sql、dll 等,覆盖组件源码、示例工程与编译脚本,便于二次开发与移植。目前已有 117 人学习下载。读者可借此研究 MyDAC 的底层实现、连接机制与组件设计,对照示例工程快速集成到自有项目中,并参考源码排查兼容性与性能问题。
1. 从一份源码包说起:MyDAC 到底解决了什么连接问题
如果你手里拿到一个名为mydac.v5.00.1.7.src.rar的压缩包,里面躺着MYSQL、MyDAC、MySql.Data src、The Client这些目录或文件,第一反应大概率是:这是一套 Delphi/C++Builder 环境下用来连 MySQL 的组件源码。MyDAC 是 Devart 系数据访问组件里专门针对 MySQL 的一支,和MySql.Data(官方 .NET 连接器)不是一回事,但很多人会把它们混着搜,因为关键词都落在“MySQL 客户端驱动”上。这个包真正值钱的地方不是“能连数据库”,而是它把客户端协议、数据集映射、连接池、事务控制这些脏活封装成了 VCL 组件,让你在 RAD 环境里拖几个控件就能跑通 CRUD。适合谁?适合还在维护 Delphi 老系统、又必须接 MySQL 5.7/8.0 的团队,以及想研究数据库客户端源码怎么分层的人。下面我按“先跑通、再拆结构、最后避坑”的顺序讲。
2. 把源码包跑起来:环境、编译与最小连接
2.1 先确认你拿到的是源码还是安装包
mydac.v5.00.1.7.src.rar从命名看是源码分发版,不是带 IDE 向导的 exe 安装包。源码版通常包含Source、Lib、Demos、Packages几类目录,The Client很可能对应客户端协议实现层,MySql.Data src可能是对官方连接器某些行为的参考或兼容层。第一步不是急着编译,而是解压后先看目录树,确认有没有*.dpk、*.dproj、*.inc这些工程文件。如果没有,说明它可能只是单元文件集合,需要你自己建包。
# 解压后先看两层目录,别一上来就全量编译 unzip -l mydac.v5.00.1.7.src.rar | head -60 # 重点找 dpk/dproj/inc,确认包结构 unzip -l mydac.v5.00.1.7.src.rar | grep -Ei '\.(dpk|dproj|inc|pas)$' | head -40逻辑说明:unzip -l只列清单不解压,避免污染工作区;grep过滤出工程文件和 Pascal 单元,判断这套源码是“开箱可编译”还是“需要手工组包”。参数上,head限制输出行数,防止刷屏。如果你用的是 7z,把unzip -l换成7z l即可,过滤逻辑不变。
2.2 编译顺序:先 Client 层,再 Dataset 层,最后 IDE 包
MyDAC 这类组件的依赖方向通常是:底层客户端协议 → 连接与命令对象 → 数据集适配 → IDE 注册包。The Client目录如果存在,优先编译它,因为上层单元会引用它的接口。常见做法是先在 IDE 里打开运行期包(Runtime Package),编译通过后再装设计期包(Design Package)。如果你跳过运行期包直接装设计期包,IDE 会报“找不到某某单元”,这就是依赖顺序翻车的典型表现。
// 最小连接示例:假设运行期包已编译并加入搜索路径 uses MyDAC.Provider, MyDAC.Connection; // 实际单元名以源码为准,这里示意分层 var Conn: TMyConnection; begin Conn := TMyConnection.Create(nil); try Conn.Server := '127.0.0.1'; Conn.Port := 3306; Conn.Username := 'app_user'; Conn.Password := 'app_pass'; Conn.Database := 'demo_db'; Conn.LoginPrompt := False; // 不弹登录框,适合服务端或自动化 Conn.Connect; // 真正建立连接 // 到这里只验证握手和认证,不涉及查询 finally Conn.Free; end; end;逻辑说明:这段代码只做“连接建立”这一件事,目的是把认证、字符集协商、端口连通性先隔离出来。参数上,LoginPrompt := False很关键,很多新手在服务里跑却弹出登录框,就是因为没关它;Port显式写 3306 而不是依赖默认值,方便后面换端口排查。如果Connect抛异常,先看错误码是 1045(认证失败)还是 2003(连不上),两者排查方向完全不同。
2.3 用 Demos 做冒烟测试,别自己先写业务
源码包里的Demos目录是最被低估的资产。它通常包含连接、查询、事务、Blob 读写这几类最小示例。我的习惯是:先编译一个最简单的Demo,把连接参数改成自己的测试库,跑通SELECT 1,再动自己的业务代码。这样能把“组件本身有问题”和“我的代码有问题”分开。如果 Demo 都跑不通,别怀疑业务逻辑,直接查环境。
-- 在测试库里先建一张最小表,避免拿业务表做实验 CREATE TABLE t_smoke ( id INT PRIMARY KEY AUTO_INCREMENT, note VARCHAR(64) NOT NULL DEFAULT '' ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; INSERT INTO t_smoke(note) VALUES ('first'); SELECT id, note FROM t_smoke;逻辑说明:utf8mb4是现在 MySQL 的推荐字符集,能存 emoji 和四字节字符;InnoDB保证事务和行锁行为可预期。先建独立测试表,是为了后面验证数据集组件的增删改查时,不污染真实数据。这一步看起来简单,但很多人跳过,结果在业务表上测出“更新丢失”,其实是自己事务没提交。
3. 拆开看结构:MyDAC 的分层与 MySql.Data 的差异
3.1 客户端协议层到底在做什么
The Client这个命名很直白,它对应的是 MySQL 客户端/服务端协议实现。这一层要处理握手包、认证插件、字符集、预处理语句、结果集编码。MySQL 8.0 默认认证插件换成了caching_sha2_password,老版本客户端库如果不支持,就会在连接阶段直接失败,报“Authentication plugin cannot be loaded”。MyDAC 源码里如果有认证插件相关单元,重点看它支持哪些插件、是否允许降级到mysql_native_password。这不是玄学,是协议版本匹配问题。
// 伪代码:观察认证插件协商的切入点 // 实际单元名和类名以源码为准,这里只表达排查思路 function NegotiateAuth(ServerCaps: Cardinal): TAuthMethod; begin if (ServerCaps and CLIENT_PLUGIN_AUTH) <> 0 then Result := ReadAuthPluginName() // 读服务端要求的插件名 else Result := amNative; // 老服务端回退 end;逻辑说明:这段不是让你照抄,而是告诉你读源码时盯哪里。CLIENT_PLUGIN_AUTH是能力位,服务端和客户端都要声明支持,才会走插件认证流程。如果你在源码里找不到对caching_sha2_password的处理,那这套老版本 MyDAC 连 MySQL 8.0 就会翻车,解决办法要么升级组件,要么在服务端给该用户单独改认证插件。
3.2 MySql.Data src 和 MyDAC 不是替代关系
很多人搜MySql.Data src是因为在 .NET 项目里用过官方连接器,看到 MyDAC 源码包里也有类似名字,就以为可以混用。实际上MySql.Data是 .NET 生态的,MyDAC 是 Delphi/C++Builder 生态的,两者 API 风格、异常体系、连接字符串格式都不同。MyDAC 的连接参数是属性式(Server、Port、Username),官方 .NET 连接器是连接字符串式(Server=...;Port=...;Uid=...)。如果你把 .NET 的连接字符串直接塞给 MyDAC,它不会报“格式错误”,而是某些属性为空,最后表现为连到了 localhost 或默认库。
| 对比项 | MyDAC(Delphi/C++Builder) | MySql.Data(.NET) |
|---|---|---|
| 配置方式 | 组件属性逐个赋值 | 连接字符串 |
| 异常类型 | 组件自定义异常 | MySqlException |
| 认证插件支持 | 取决于组件版本 | 取决于连接器版本 |
| 适用场景 | VCL 桌面/服务端 | .NET 应用 |
逻辑说明:这张表不是让你背,而是帮你判断“我到底该用哪套”。如果你在 Delphi 里,就别去翻MySql.Data的文档;如果你在 .NET 里,MyDAC 源码对你帮助有限。混用最典型的翻车是:连接字符串里写了SslMode=Required,但 MyDAC 对应属性没设,结果以为加密了其实没加密。
3.3 数据集组件与连接池的配合
MyDAC 的数据集组件(类似TMyQuery、TMyTable)不是孤立工作的,它们背后共享连接或使用独立连接。连接池的行为直接影响“为什么我改了数据但另一个查询看不到”。常见做法是:短连接场景开池,长事务场景关池或显式控制事务边界。如果你在源码里看到Pooling、MaxPoolSize这类属性,说明它支持池化。池化的坑在于:连接归还时如果没有回滚未提交事务,下一个拿到该连接的人会看到脏数据。
// 事务边界要显式,别依赖组件自动提交 Conn.StartTransaction; try Qry.SQL.Text := 'UPDATE t_smoke SET note = :n WHERE id = :id'; Qry.ParamByName('n').AsString := 'updated'; Qry.ParamByName('id').AsInteger := 1; Qry.ExecSQL; Conn.Commit; // 不 Commit,池化连接归还后事务仍挂着 except Conn.Rollback; raise; end;逻辑说明:StartTransaction和Commit/Rollback必须成对出现,try...except保证异常时回滚。参数化查询用ParamByName而不是拼字符串,避免 SQL 注入和引号转义问题。如果你用池化又忘了回滚,下一个请求可能读到未提交数据,这种问题在测试环境很难复现,因为并发不够。
4. 避坑与排查:连接失败、乱码、事务不生效
4.1 现象:连接报“Authentication plugin cannot be loaded”
原因:MySQL 8.0 用户默认用caching_sha2_password,而老版本 MyDAC 客户端库只认mysql_native_password。解决:要么升级 MyDAC 到支持新插件的版本,要么在服务端执行ALTER USER 'app_user'@'%' IDENTIFIED WITH mysql_native_password BY 'app_pass';。注意改插件后要FLUSH PRIVILEGES。这不是长久之计,但能让你先把系统跑起来。
4.2 现象:中文存进去变成问号
原因:连接字符集没设对,或者表/列字符集不是utf8mb4。解决:连接属性里显式设Charset := 'utf8mb4',同时确认表定义。如果源码里字符集是硬编码的latin1,那就得改源码或找配置项。排查时用SHOW VARIABLES LIKE 'character_set%';看服务端,用SELECT HEX(note) FROM t_smoke;看实际存储字节。
4.3 现象:更新后查询看不到变化
原因:事务没提交,或者连接池复用了带未提交事务的连接。解决:检查代码里有没有漏掉Commit;如果用了池化,在归还连接前确保事务已结束。可以在测试时临时关掉池化,看问题是否消失,以此定位。
4.4 现象:编译时报“Unit not found”
原因:源码包的搜索路径没配全,或者运行期包没先编译。解决:在 IDE 的 Library Path 里把Source下所有子目录加进去;先编译运行期包,再装设计期包。如果还不行,看报错的具体单元名,去目录里搜它在哪个子目录,单独把那个目录加进搜索路径。
4.5 现象:Demo 能跑,自己的项目连不上
原因:Demo 用的连接参数和你的不同,或者你的项目引用了不同版本的单元。解决:把 Demo 的连接参数逐项抄过来,先跑通再改。检查项目里有没有重复引用旧版单元,Delphi 的单元搜索顺序有时会让人抓狂,用Project > Options > Delphi Compiler > Search Path确认顺序。
5. 进阶用法:从源码里挖出可复用的连接管理技巧
源码包最大的价值不是“能用”,而是“能改”。当你把The Client层的连接建立流程读一遍,会发现它把“解析服务端能力位 → 选择认证方式 → 协商字符集 → 建立会话”拆得很清楚。你可以把这套流程抽出来,做成自己的轻量连接工厂,不依赖 IDE 组件,方便在控制台程序或服务里复用。我一般会先找连接类的Connect方法,顺着调用链看它做了哪几件事,然后写一个最小复现,把每一步的返回值打日志。这样即使以后换组件,排查思路还在。
// 伪代码:把连接建立的关键节点打日志,方便定位卡在哪一步 procedure TConnTrace.Connect; begin Log('1. resolve host'); ResolveHost; Log('2. tcp connect'); OpenSocket; Log('3. read handshake'); ReadHandshake; Log('4. send auth'); SendAuth; Log('5. read auth result'); ReadAuthResult; Log('6. set charset'); SetCharset; Log('connected'); end;逻辑说明:这段伪代码的重点是“分步日志”。真实源码里这些步骤可能揉在一个方法里,但你可以通过断点或日志把它们分开。参数上,每一步的耗时和返回值都值得记录,尤其是ReadAuthResult返回的错误码。这样下次再遇到连接问题,你一眼就能看出是网络层、认证层还是字符集层。
另一个进阶点是连接池的健康检查。池化连接放久了可能被服务端wait_timeout断开,下次取出来用就报“MySQL server has gone away”。常见做法是在取出连接时先执行一次轻量SELECT 1,失败就重建。这个逻辑如果源码里没有,你可以自己在封装层加。
| 检查项 | 建议值 | 说明 |
|---|---|---|
| 连接超时 | 5~10 秒 | 太短易误判,太长卡界面 |
| 命令超时 | 30 秒 | 按业务查询复杂度调 |
| 池大小 | 5~20 | 按并发量调,别盲目开大 |
| 空闲回收 | 60~300 秒 | 小于服务端 wait_timeout |
逻辑说明:这张表是我在多个项目里试出来的经验值,不是标准答案。连接超时设太短,网络抖动就失败;池大小开太大,数据库连接数爆掉。空闲回收一定要小于服务端的wait_timeout,否则池里全是死连接。
最后说一个我踩过的坑:有次为了省事,直接把源码包里某个单元的初始化代码复制到自己的项目,结果它依赖另一个单元的全局变量,编译能过,运行时却随机崩溃。后来老老实实按依赖顺序引单元,问题消失。所以读源码可以,抄代码要连依赖一起看。希望帮到你。
本文还有配套的精品资源,点击获取