
简介ADO2.20 Class 是一套面向 Visual C 开发者的 ADO 数据库访问类库由国外开发者在 CodeProject 发布旨在简化 VC 程序对 SQL Server、Oracle、Access 等数据源的访问。它封装了 Connection、Command、Recordset 等核心对象让开发者无需深入底层接口即可完成连接、查询与结果集处理适合需要快速集成数据库功能的中级 C 程序员。资源包共 3 个文件包含 ado2.cpp 与 ado2.h 两个源码文件分别提供类实现与接口声明另附一份 MHT 格式的说明文档记录原始使用指南与示例压缩包约 105KB体积轻巧便于直接加入工程。目前已有 124 人学习下载。通过阅读源码读者可理解 ADO 对象在 VC 中的封装思路掌握连接字符串配置、SQL 执行与记录遍历等关键操作并可直接复用该类库加速业务开发对不熟悉 ADO 或追求开发效率的开发者具有较高参考价值。1. ADO2.20 Class从连接字符串到记录集一次把老数据访问层讲透接手一套跑了七八年的老系统时你大概率会在某个.pas或.cpp文件里撞见ADO2.20 Class这样的字眼。它不是某个新框架而是 Delphi、C Builder 这类 RAD 工具里对 Microsoft ActiveX Data Objects 2.20 类型库的引用名——导入后 IDE 会生成一套TADOConnection、TADOQuery、TADODataset之类的封装类。很多人第一次看到它是因为要改一个连 SQL Server 2000 或 Access 的旧模块结果发现连接串、游标位置、锁类型全是坑。这篇笔记就按我实际维护老项目的路径走先讲清 ADO2.20 Class 到底封装了什么再落到连接、查询、参数、事务的具体写法最后把那些年翻过的车一条条列出来。适合手里有遗留系统、需要在不重写的前提下把数据访问层稳住的人。2. ADO2.20 Class 的封装边界它替你做了什么又没做什么2.1 类型库导入后生成的类层次在 Delphi 里通过Project Import Type Library选择Microsoft ActiveX Data Objects 2.20 LibraryIDE 会生成一个ADODB_TLB.pasC Builder 下是ADODB_TLB.h。这个单元里最核心的几个接口是_Connection、_Recordset、_Command而TADOConnection等 VCL 组件只是对它们的二次封装。理解这一点很关键你调TADOQuery.Open时底层走的是_Recordset.Open参数最终拼进_Command的Parameters集合。常见做法是直接用 VCL 组件因为可视化拖拽快但如果你在写非可视化的服务端逻辑直接CoConnection.Create反而更干净少一层消息循环依赖。我一般会先确认项目里引用的到底是 2.20 还是 2.10——两者在CursorLocation的默认值和CommandTimeout行为上有细微差别混用会导致同一段代码在开发机跑通、上服务器就超时。2.2 连接字符串的三种写法与选型连接字符串决定了你后面所有查询的稳定性和性能。ADO2.20 支持 ODBC 风格和 OLE DB 风格老项目里最常见的是 OLE DB// 方式一OLE DB 直连 SQL Server指定 Provider ADOConnection1.ConnectionString : ProviderSQLOLEDB.1; Persist Security InfoFalse; User IDsa;Passwordxxx; Initial CatalogOrderDB; Data Source192.168.1.10,1433;;// 方式二走 ODBC 驱动兼容性更好但性能略低 ADOConnection1.ConnectionString : ProviderMSDASQL.1; Persist Security InfoFalse; Extended PropertiesDSNOrderDSN;UIDsa;PWDxxx;;;// 方式三Access 本地文件注意路径不能有中文 ADOConnection1.ConnectionString : ProviderMicrosoft.Jet.OLEDB.4.0; Data SourceC:\Data\Order.mdb; Persist Security InfoFalse;;逻辑说明Provider是第一个必须对的参数SQLOLEDB.1 对应 SQL Server 2000/2005MSDASQL.1 是 ODBC 桥接Jet.OLEDB.4.0 只适合 Access 2003 及以前。Persist Security InfoFalse能避免密码在连接打开后被外部读取安全上建议保持 False。Data Source里带端口时用逗号分隔这是 SQL Server 的写法写成冒号会直接报“找不到服务器”。参数怎么改如果服务器开了命名实例Data Source要写成机器名\实例名如果走防火墙确认 1433 或自定义端口已放行。ConnectionTimeout默认 15 秒内网可以调到 5 秒快速失败跨机房反而要加到 30 秒。2.3 游标位置与锁类型的组合影响CursorLocation和LockType是 ADO2.20 里最容易被忽略、又最容易导致“查询卡死”的两个属性。CursorLocation设为clUseClient时记录集全部拉到客户端适合小结果集、需要离线排序的场景设为clUseServer时游标留在服务器适合大结果集但会占用服务器连接资源。// 客户端游标 乐观锁读多写少、结果集小于 5000 行 ADOQuery1.CursorLocation : clUseClient; ADOQuery1.LockType : ltOptimistic; ADOQuery1.Open; // 服务器游标 只读报表类大查询 ADOQuery1.CursorLocation : clUseServer; ADOQuery1.LockType : ltReadOnly; ADOQuery1.Open;逻辑说明clUseClient下 ADO 会把数据一次性取回本地RecordCount能立刻拿到准确值clUseServer下RecordCount可能返回 -1需要SELECT COUNT(*)单独查。ltOptimistic在提交时检查版本冲突适合并发不高的订单表ltBatchOptimistic配合UpdateBatch可以批量提交但老版本 ADO 在断线重连后容易丢批次。参数怎么改如果查询超过 10 万行优先clUseServerltReadOnly否则客户端内存会爆。如果必须用clUseClient把MaxRecords设成 50000 做保护超出的部分分页取。3. 用 ADO2.20 Class 跑通增删改查的最小闭环3.1 参数化查询别再用字符串拼接老代码里十有八九是SELECT * FROM Users WHERE Name Edit1.Text 这种写法在 ADO2.20 下不仅慢还会因为单引号、日期格式直接报错。正确做法是用Parameters集合ADOQuery1.SQL.Text : SELECT UserID, UserName, CreateTime FROM Users WHERE DeptID :DeptID AND CreateTime :StartTime; ADOQuery1.Parameters.ParamByName(DeptID).Value : 10; ADOQuery1.Parameters.ParamByName(StartTime).Value : EncodeDate(2024, 1, 1); ADOQuery1.Open;逻辑说明:DeptID是命名参数ADO 会把它编译成?占位符再绑定避免 SQL 注入。Value赋值时 Delphi 的TDateTime会自动转成 OLE 日期不需要手动格式化。如果参数是字符串直接赋string即可ADO 会按VarChar处理。参数怎么改如果参数可能为空用Parameters.ParamByName(X).Value : Null不要赋空字符串否则WHERE X 会漏掉 NULL 行。批量插入时把Prepared设为 True循环里只改参数值再ExecSQL能省掉重复解析 SQL 的开销。3.2 事务与错误回滚的写法ADO2.20 的事务通过TADOConnection.BeginTrans / CommitTrans / RollbackTrans控制。血泪经验是一定要在try...except里回滚否则连接池里会残留未提交事务后续查询全部阻塞。ADOConnection1.BeginTrans; try ADOQuery1.SQL.Text : UPDATE Account SET Balance Balance - 100 WHERE ID 1; ADOQuery1.ExecSQL; ADOQuery2.SQL.Text : UPDATE Account SET Balance Balance 100 WHERE ID 2; ADOQuery2.ExecSQL; ADOConnection1.CommitTrans; except on E: Exception do begin ADOConnection1.RollbackTrans; raise; // 继续往上抛让调用方知道失败 end; end;逻辑说明BeginTrans之后所有在同一连接上的操作都在一个事务里CommitTrans才真正落盘。RollbackTrans在异常时撤销。注意raise不能省否则上层以为成功。参数怎么改如果事务里包含大量插入把ADOConnection1.IsolationLevel设为ilReadCommitted减少锁竞争如果要求可重复读用ilRepeatableRead但会加大死锁概率。CommandTimeout在事务里要单独设默认 30 秒可能不够。3.3 记录集遍历与字段取值ADOQuery1.Open之后用while not ADOQuery1.Eof do遍历。字段取值优先用FieldByName(X).AsInteger / AsString / AsDateTime不要用Fields[0].Value因为列顺序一变就全错。ADOQuery1.Open; while not ADOQuery1.Eof do begin Memo1.Lines.Add(Format(%d - %s - %s, [ ADOQuery1.FieldByName(UserID).AsInteger, ADOQuery1.FieldByName(UserName).AsString, FormatDateTime(yyyy-mm-dd, ADOQuery1.FieldByName(CreateTime).AsDateTime) ])); ADOQuery1.Next; end;逻辑说明AsInteger在字段为 NULL 时返回 0AsString返回空串AsDateTime返回 0 日期。如果业务上要区分 NULL 和 0先用FieldByName(X).IsNull判断。参数怎么改遍历大结果集时把ADOQuery1.DisableControls和EnableControls包在循环外能显著减少 DBGrid 重绘。如果只需要读一遍用GetRows一次性取数组比逐行Next快。4. ADO2.20 Class 避坑排查五条现场记录4.1 现象连接打开成功但查询报“未指定的错误”原因CursorLocation默认是clUseClient而某些老 OLEDB 驱动在客户端游标下不支持JOIN或ORDER BY的某些组合。解决把CursorLocation显式设为clUseServer或者换MSDASQL.1走 ODBC。我遇到过 SQL Server 2000 上LEFT JOIN加ORDER BY必现改服务器游标后消失。4.2 现象RecordCount返回 -1分页逻辑全乱原因服务器游标下 ADO 不预取总行数。解决要么改clUseClient要么单独SELECT COUNT(*)。如果坚持服务器游标用ADOQuery1.Recordset.PageSize和AbsolutePage做分页但要注意PageSize设太小会频繁往返。4.3 现象多线程里每个线程各建一个TADOConnection跑一会儿就报“连接池耗尽”原因ADO2.20 默认走 OLEDB 服务组件连接池大小有限且TADOConnection不是线程安全的。解决每个线程独立CoInitialize连接串里加OLE DB Services-4禁用池化或者改用TADOConnection的Clone方法共享底层连接。更稳的做法是限制并发线程数别超过 10 个。4.4 现象Access 数据库插入中文变问号原因Jet.OLEDB.4.0 默认按 ANSI 处理而 Delphi 的string是 Unicode。解决连接串加Extended PropertiesCharacterSet65001;或者把字段类型改成nvarchar。如果还是不行用WideString显式转换参数值。4.5 现象UpdateBatch提交后部分行没更新也不报错原因ltBatchOptimistic下如果某行在提交前被其他连接修改ADO 会静默跳过冲突行。解决提交后检查Recordset.Status或者改用ltOptimistic逐行提交。批量场景建议加WHERE条件带版本号字段手动检测冲突。5. 把 ADO2.20 Class 用稳的进阶习惯5.1 连接复用与超时参数表老项目里最常见的性能问题不是 SQL 慢而是每次查询都Open再Close连接。正确做法是全局一个TADOConnection所有TADOQuery共享它。下面是我在多个遗留系统里验证过的参数组合参数内网 OLTP跨机房报表Access 本地ConnectionTimeout53010CommandTimeout1512030CursorLocationclUseClientclUseServerclUseClientLockTypeltOptimisticltReadOnlyltOptimisticMaxRecords500000不限10000MaxRecords设为 0 表示不限制但报表查询建议还是设一个上限防止误查全表把内存打满。CommandTimeout在跨机房时要给足否则网络抖动直接超时。5.2 用TADODataSet替代TADOQuery做只读缓存如果某个基础数据比如部门列表、字典表在程序启动后几乎不变用TADODataSet加clUseClient一次性加载然后Disconnect断开连接后续从内存读。这样既减少数据库压力又避免连接被占用。ADODataSet1.Connection : ADOConnection1; ADODataSet1.CursorLocation : clUseClient; ADODataSet1.CommandText : SELECT DeptID, DeptName FROM Department; ADODataSet1.Open; ADODataSet1.Disconnect; // 断开后数据仍在本地逻辑说明Disconnect后记录集变成离线状态Locate、Filter都能用但不能Requery。适合配置类数据。参数怎么改如果数据量超过 1 万行别用这招内存吃不消。Disconnect前确认CacheSize够大默认 1 会频繁取数。5.3 一个我坚持了多年的检查习惯每次改完 ADO2.20 相关代码我一定会做三件事第一把连接串里的密码换成测试账号确认没有硬编码第二在try...except里加OutputDebugString打印E.Message因为老 IDE 的调试器有时吞异常第三用ADOConnection1.Errors集合看底层 OLEDB 错误VCL 抛出的异常往往只保留最后一条Errors里才有完整堆栈。这个习惯帮我省了无数次“明明改了却还报旧错”的后悔药。希望帮到你。本文还有配套的精品资源点击获取