ADO2.20 Class 封装实战:连接字符串、参数化查询与事务避坑指南
简介ADO2.20 Class 是一套面向 Visual C 开发者的 ActiveX Data Objects 类库由非中文开发者在 CodeProject 上发布旨在简化 VC 程序对 SQL Server、Oracle、Access 等数据源的访问让开发者无需深入底层 ADO 接口即可完成数据库操作。资源包共 3 个文件包含 1 个 cpp 实现文件、1 个 h 头文件以及 1 个 mht 说明文档压缩包约 105KB体积轻巧便于集成到现有工程。其中头文件提供类定义与函数声明cpp 文件封装了 Connection、Command、Recordset 等核心对象的常用操作mht 文档则保留了原版使用说明与示例方便对照查阅。目前已有 126 人学习下载适合不熟悉 ADO 或需要快速为 VC 项目添加数据库访问能力的开发者参考可借此理解连接字符串配置、SQL 执行与结果集遍历的封装思路并直接复用源码缩短开发周期。1. ADO2.20 Class从连接字符串到记录集一条被忽视的稳定链路很多人第一次接触 ADO2.20 Class是在维护一套老系统的数据访问层。代码里到处是ADODB.Connection、ADODB.Recordset没有 ORM没有连接池封装甚至连参数化查询都是手写的。你可能会觉得这套东西早该淘汰了但现实是大量工业控制、医疗设备、财务对账类软件至今仍跑在 ADO 2.20 之上原因很简单——它足够薄薄到你能完全掌控每一次数据库交互的字节流向。ADO2.20 Class 并不是某个具体类库的名字而是围绕 ADO 2.20 版本组件封装出来的一套数据访问类。它解决的核心问题是把 Connection、Command、Recordset 三个对象的生命周期管理、错误处理和类型转换收敛到一个可复用的类里避免在每个业务函数里重复写Open、Close、Set conn Nothing。适合谁适合那些需要维护或重构 VB6、VBScript、早期 ASP、Delphi 遗留系统又不想引入重型框架的工程师。接下来的内容我会按“先跑通最小链路再拆参数再讲避坑最后给一个验证技巧”的顺序展开每一步都能直接抄。2. 把 ADO2.20 Class 跑起来最小可用封装与连接字符串的四种写法2.1 为什么不用原生 ADODB 而要多封一层原生 ADODB 用起来不难难在一致性。一个项目里如果有二十个地方打开连接就有二十种错误处理风格有的用On Error Resume Next吞掉异常有的在Err.Number判断后直接Resume还有的干脆不关连接。ADO2.20 Class 的价值不在于性能提升而在于把“打开-执行-关闭”这个固定动作变成不可绕过的模板。我一般会封一个CDataAccess类暴露三个方法Execute用于无返回集的增删改Query返回一个二维数组或 RecordsetBeginTrans/CommitTrans/RollbackTrans用于事务。内部持有ADODB.Connection和可选的ADODB.Command。这样做的好处是当你要从 SQL Server 换到 Access 或 Oracle 时只需要改连接字符串和少量方言业务层代码不动。2.2 最小可用类的代码骨架下面是一个 VB6 风格的类模块骨架去掉了具体业务逻辑只保留 ADO2.20 的核心调用。如果你用 VBScript 或 VBA把Private换成Dim把Class_Initialize换成普通初始化函数即可。 CDataAccess.cls Private m_conn As ADODB.Connection Private m_cmd As ADODB.Command Private Sub Class_Initialize() Set m_conn New ADODB.Connection Set m_cmd New ADODB.Command m_conn.CursorLocation adUseClient 客户端游标便于断开后继续用 Recordset m_conn.CommandTimeout 30 秒防止长查询卡死界面 End Sub Public Sub OpenConnection(ByVal connStr As String) On Error GoTo ErrHandler If m_conn.State adStateOpen Then m_conn.Close m_conn.Open connStr Exit Sub ErrHandler: Err.Raise Err.Number, CDataAccess.OpenConnection, Err.Description End Sub Public Function Query(ByVal sql As String) As ADODB.Recordset Dim rs As ADODB.Recordset Set rs New ADODB.Recordset rs.CursorLocation adUseClient rs.Open sql, m_conn, adOpenStatic, adLockReadOnly Set Query rs End Function Public Sub Execute(ByVal sql As String) m_conn.Execute sql, , adExecuteNoRecords End Sub Private Sub Class_Terminate() If Not m_conn Is Nothing Then If m_conn.State adStateOpen Then m_conn.Close Set m_conn Nothing End If End Sub逻辑说明CursorLocation设为adUseClient是关键一步。ADO 2.20 默认使用服务器端游标Recordset 一旦断开连接就不可用改成客户端游标后Recordset 会一次性把数据拉到本地断开后仍可遍历。代价是内存占用上升所以只适合结果集不大的场景。CommandTimeout默认是 30 秒但很多老系统在慢查询时直接卡死显式设成 30 或 60 能避免无限等待。参数说明adOpenStatic表示静态游标适合只读遍历adLockReadOnly表示不锁定记录减少数据库压力。如果你需要更新改成adLockOptimistic但要注意并发冲突。2.3 连接字符串的四种写法与选择依据连接字符串是 ADO2.20 Class 最容易翻车的地方。同一个数据库不同写法在超时、编码、权限上表现完全不同。下面这张表是我在维护老系统时总结的对比以 SQL Server 为例Access 和 Oracle 的差异在备注里说明。写法示例片段适用场景注意点OLE DB ProviderProviderSQLOLEDB;Data Source...传统 ADO 2.20 默认需要装 MDACWin10 以上可能缺失ODBC DriverDriver{SQL Server};Server...兼容性最好性能略低但几乎不会因组件缺失失败DSN 文件FileDSNxxx.dsn多环境切换部署时要带 dsn 文件路径不能写死无 DSN 的 ODBCDriver{SQL Server Native Client 11.0};...新驱动驱动版本要和目标机器一致Access 数据库用ProviderMicrosoft.Jet.OLEDB.4.0或Microsoft.ACE.OLEDB.12.0后者支持 64 位但需要单独安装。Oracle 用ProviderOraOLEDB.Oracle注意Data Source写的是 TNS 别名而不是 IP。提示连接字符串里的User ID和Password不要硬编码在类里用配置文件或环境变量传入。老系统常见做法是写在一个.ini里类初始化时读取。2.4 用 Command 对象做参数化查询直接拼接 SQL 是 ADO2.20 Class 最大的安全隐患。正确做法是用ADODB.Command加Parameters。下面是一个按 ID 查询的示例Public Function QueryById(ByVal id As Long) As ADODB.Recordset Dim rs As ADODB.Recordset Set rs New ADODB.Recordset With m_cmd .ActiveConnection m_conn .CommandText SELECT * FROM Orders WHERE OrderID ? .CommandType adCmdText .Parameters.Refresh 让 Provider 推断参数类型 .Parameters(0).Value id End With rs.CursorLocation adUseClient rs.Open m_cmd, , adOpenStatic, adLockReadOnly Set QueryById rs End Function逻辑说明Parameters.Refresh会向数据库请求参数元数据对于 SQL Server 和 Oracle 都有效但对某些 Access 驱动可能返回空。如果 Refresh 失败就手动CreateParameter并指定类型。?是 OLE DB 的占位符不是所有驱动都支持命名参数所以统一用问号最稳。参数说明adCmdText表示命令文本是 SQL 语句如果调用存储过程改成adCmdStoredProc参数用p1或直接按顺序传。注意Parameters(0)的索引从 0 开始不是 1。3. ADO2.20 Class 的四个必调参数与事务处理3.1 CursorLocation 选客户端还是服务器端这是 ADO2.20 里最容易被忽略、又最影响稳定性的参数。服务器端游标adUseServer在连接断开后 Recordset 立即失效而且对数据库的锁持有时间更长。客户端游标adUseClient把数据缓存在本地断开后仍可读适合“取数据-关连接-慢慢处理”的模式。但客户端游标不是银弹。如果结果集超过几万行内存会飙升而且RecordCount虽然准确但代价是全部拉取。我的经验是报表类查询用客户端游标逐行处理的大批量更新用服务器端游标加只进游标adOpenForwardOnly。3.2 LockType 与并发冲突的取舍adLockReadOnly最安全不锁记录。adLockOptimistic只在更新瞬间锁记录适合并发不高的场景。adLockBatchOptimistic配合UpdateBatch可以批量提交减少网络往返但冲突时要自己处理Status字段。老系统里常见的一个坑是用adLockOptimistic打开 Recordset然后遍历修改最后UpdateBatch。如果中途有人改了同一行UpdateBatch会报错但错误信息很模糊。解决方法是遍历后检查rs.Status对adRecConcurrencyViolation的记录重新读取再合并。3.3 CommandTimeout 与 ConnectionTimeout 的区别ConnectionTimeout控制建立连接的超时默认 15 秒。CommandTimeout控制命令执行超时默认 30 秒。很多开发者只设了前者结果查询慢的时候界面卡死几十秒。正确做法是两个都显式设置并且CommandTimeout根据业务设成 60 或 120。注意CommandTimeout设为 0 表示无限等待生产环境不要这么干否则一个慢查询能把线程池占满。3.4 事务处理的正确姿势ADO2.20 的事务用Connection.BeginTrans、CommitTrans、RollbackTrans。关键点是事务必须绑定在同一个 Connection 上不能跨连接。下面是一个转账的伪代码示例Public Sub Transfer(ByVal fromId As Long, ByVal toId As Long, ByVal amount As Currency) On Error GoTo ErrHandler m_conn.BeginTrans m_conn.Execute UPDATE Accounts SET Balance Balance - amount WHERE AccountID fromId m_conn.Execute UPDATE Accounts SET Balance Balance amount WHERE AccountID toId m_conn.CommitTrans Exit Sub ErrHandler: m_conn.RollbackTrans Err.Raise Err.Number, CDataAccess.Transfer, Err.Description End Sub逻辑说明这里为了演示简化了 SQL 拼接实际应该用 Command 参数化。BeginTrans之后的所有Execute都在同一事务里任何一步出错就RollbackTrans。注意RollbackTrans本身也可能出错所以最好再包一层On Error Resume Next。参数说明BeginTrans返回一个隔离级别默认是adXactReadCommitted。如果需要更高级别传adXactSerializable但会降低并发。4. ADO2.20 Class 避坑记录五个让老系统半夜报警的细节4.1 现象Recordset 关闭后仍占用内存进程越来越大原因客户端游标把数据缓存在本地Recordset.Close只是释放了游标但底层Fields集合和Properties集合可能还持有引用。如果循环里反复创建 Recordset 而不显式Set rs Nothing内存不会及时回收。解决每次用完 Recordset 后先rs.Close再Set rs Nothing。在类模块里把 Recordset 作为局部变量而不是成员变量让它在函数退出时自动释放。4.2 现象连接字符串在开发机正常部署到客户机报“未找到提供程序”原因开发机装了 MDAC 或 SQL Native Client客户机只有系统自带的 ODBC 驱动。ProviderSQLOLEDB在 Win10 以上可能不存在。解决改用 ODBC 写法Driver{SQL Server};...或者把驱动安装包一起部署。更稳妥的做法是在类初始化时用On Error尝试多种连接字符串取第一个成功的。4.3 现象参数化查询报“参数类型不匹配”原因Parameters.Refresh推断的类型和实际传入的值不一致。比如数据库字段是Decimal你传了Double某些驱动会拒绝。解决手动CreateParameter并指定adDecimal或adNumeric设置Precision和Scale。或者统一用adVarWChar传字符串让数据库隐式转换但这样会失去索引优势。4.4 现象事务提交后数据没生效或者部分生效原因Connection在执行BeginTrans之前已经隐式开启了一个事务或者Execute语句里包含了COMMIT。另外如果用了连接池CommitTrans可能被池化层拦截。解决确保BeginTrans之前没有未提交的操作。不要在 SQL 里写BEGIN TRAN。如果用了 MTS 或 COM事务要交给容器管理不要手动BeginTrans。4.5 现象多线程环境下 ADO2.20 Class 随机崩溃原因ADO 的 Connection 和 Recordset 不是线程安全的。VB6 的单元线程模型里每个线程要自己创建 Connection不能共享。解决每个线程独立创建CDataAccess实例或者用对象池加锁。如果必须共享用ADODB.Connection的Marshal接口但复杂度很高不如改成每个线程一个连接。5. 用 ADO2.20 Class 做一次可复现的验证从建表到断连遍历5.1 验证目标与准备这一章给一个完整的验证流程用来确认你的 ADO2.20 Class 封装是否真的能在断开连接后继续使用 Recordset。准备一个空的 Access 数据库或 SQL Server 库建一张TestItems表字段ID自增Name文本。CREATE TABLE TestItems ( ID INT IDENTITY(1,1) PRIMARY KEY, Name NVARCHAR(50) ); INSERT INTO TestItems (Name) VALUES (Alpha), (Beta), (Gamma);5.2 断开连接后遍历 Recordset 的代码Public Sub TestDisconnectedRecordset() Dim da As New CDataAccess Dim rs As ADODB.Recordset da.OpenConnection ProviderSQLOLEDB;Data Source.;Initial CatalogTestDB;Integrated SecuritySSPI; Set rs da.Query(SELECT * FROM TestItems ORDER BY ID) 关键关闭连接模拟断开 da.CloseConnection Do While Not rs.EOF Debug.Print rs.Fields(ID).Value, rs.Fields(Name).Value rs.MoveNext Loop rs.Close Set rs Nothing Set da Nothing End Sub逻辑说明Query内部用了adUseClient所以CloseConnection之后 Recordset 仍然可用。如果这里报“连接已关闭”说明 CursorLocation 没设对。Debug.Print在 VB6 里输出到立即窗口VBA 里同样可用。参数说明Integrated SecuritySSPI表示用 Windows 身份验证不需要用户名密码。如果要用 SQL 验证改成User IDsa;Passwordxxx。5.3 验证事务回滚是否彻底再写一个测试插入一条记录然后故意让第二条插入失败检查第一条是否回滚。Public Sub TestRollback() Dim da As New CDataAccess da.OpenConnection ProviderSQLOLEDB;Data Source.;Initial CatalogTestDB;Integrated SecuritySSPI; On Error GoTo ErrHandler da.BeginTrans da.Execute INSERT INTO TestItems (Name) VALUES (Delta) da.Execute INSERT INTO TestItems (ID, Name) VALUES (1, Epsilon) 主键冲突 da.CommitTrans Exit Sub ErrHandler: da.RollbackTrans Debug.Print Rolled back: Err.Description End Sub运行后查TestItems表应该只有 Alpha、Beta、Gamma 三条没有 Delta。如果 Delta 还在说明RollbackTrans没生效检查是否在BeginTrans之前有未提交的操作。5.4 一个容易被忽略的收尾习惯每次验证完把CDataAccess实例置空并且确认数据库里的连接数没有残留。在 SQL Server 里可以查sys.dm_exec_sessions看有没有状态为sleeping的会话。如果有说明Class_Terminate里的Close没执行可能是对象被循环引用卡住了。我自己的习惯是在类里加一个Dispose方法显式关闭连接和清空对象然后在业务代码的Finally块里调用。不要依赖Class_Terminate因为 VB6 的引用计数在循环引用时会失效。这个习惯帮我省掉了无数次半夜被告警叫醒的麻烦。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

量能跌破1.5万亿:牛没死,死的是惯性

量能跌破1.5万亿:牛没死,死的是惯性

《量能跌破1.5万亿:牛没死,死的是惯性》——修复看业绩,反转看量能;这一轮周期里,耐心是新的仓位六月,A股一天成交三万亿,热闹得像除夕。九月最后两个交易日,不到一万五千亿&#xf…

2026/10/12 17:32:56 阅读更多 →
前端工具集ztools:按需引入、tree-shaking与核心函数实现

前端工具集ztools:按需引入、tree-shaking与核心函数实现

简介:ztools 是一套面向 JavaScript 前端开发者的轻量级工具集,适合希望提升异步编程、视图渲染与代码解耦能力的初中级开发者。它围绕三个核心模块展开:兼容旧版 IE 的 ES6 Promise 实现,帮助在低版本浏览器中编写现代异步代码&a…

2026/10/12 17:32:56 阅读更多 →
AutoCAD SHX字体缺失导致DWG乱码的解决与字体映射方案

AutoCAD SHX字体缺失导致DWG乱码的解决与字体映射方案

简介:一套面向 AutoCAD 用户的 SHX 字体合集,专门解决 DWG 图纸打开时字体缺失、乱码或显示为问号的问题,适合设计、制图与工程技术人员在日常出图和交流图纸时使用。压缩包共包含 357 个文件,其中 351 个 SHX 形字体文件为主体&a…

2026/10/12 17:32:56 阅读更多 →

最新新闻

从Qwen3-Coder-Next到GPT-5.2:开源编程模型与API提速的开发者落地指南

从Qwen3-Coder-Next到GPT-5.2:开源编程模型与API提速的开发者落地指南

2026年2月4日的AI早报,值得单独拆开聊一聊的话题有两个:一个是通义千问发布了开源编程模型 Qwen3-Coder-Next,另一个是发布 GPT-5.2 系列的海外AI研究机构宣布模型整体提速 40%。这两个消息放在同一天,表面上只是一个“上新”加一…

2026/10/12 22:34:12 阅读更多 →
写活一代人:《六零后赋·甲子铸魂》的创作技法与赋体应用

写活一代人:《六零后赋·甲子铸魂》的创作技法与赋体应用

这些年写人物题材的作品,我有个很深的体会:写活一代人,比写活一个人更难。《六零后赋甲子铸魂》这个标题一进入视野,就让我停住了——不是因为它用了“赋”这个古老文体,而是“甲子铸魂”这四个字把时间与精神拧在了一…

2026/10/12 22:34:12 阅读更多 →
Python反射机制详解:动态获取与调用对象属性的方法

Python反射机制详解:动态获取与调用对象属性的方法

1. 什么是反射?它不是“照镜子”,而是程序的自我认知能力很多人第一次听到“Python反射”这个词,下意识会联想到物理课上的光反射,或者手机前置摄像头里那个熟悉的自己——但编程里的反射,和这些都没关系。它本质上是一…

2026/10/12 22:34:12 阅读更多 →
Chrome Samples 技术解析:ES2015 引入的 TypedArray 方法——from、of 静态方法与 18 个镜像实例方法

Chrome Samples 技术解析:ES2015 引入的 TypedArray 方法——from、of 静态方法与 18 个镜像实例方法

示例工程 【免费下载链接】samples A repo containing samples tied to new functionality in each release of Google Chrome. 项目地址: https://gitcode.com/gh_mirrors/samp/samples 点击查看 免费下载 导读 本文围绕 Google Chrome Samples 仓库中的 typedar…

2026/10/12 22:33:12 阅读更多 →
WebGPU 的 primitive_index 内置变量:从提案到 WGSL 规范的逐片元索引实战指南

WebGPU 的 primitive_index 内置变量:从提案到 WGSL 规范的逐片元索引实战指南

图形学 【免费下载链接】gpuweb Where the GPU for the Web work happens! 项目地址: https://gitcode.com/gh_mirrors/gp/gpuweb 点击查看 免费下载 本文基于 gpuweb 仓库中的 primitive-index 提案文档 及其在 WebGPU/WGSL 规范中的落地实现,系统讲解…

2026/10/12 22:33:12 阅读更多 →
go-billy v5 深入解析:为 Go 提供缺失的文件系统抽象层

go-billy v5 深入解析:为 Go 提供缺失的文件系统抽象层

云原生容器运行时 【免费下载链接】cri-o Open Container Initiative-based implementation of Kubernetes Container Runtime Interface 项目地址: https://gitcode.com/gh_mirrors/cr/cri-o 点击查看 免费下载 导读:go-billy 是 go-git 项目&#xff…

2026/10/12 22:33:11 阅读更多 →

日新闻

复古胶片颗粒感噪点合成器:Canvas ImageData 像素高斯杂色注入算法

复古胶片颗粒感噪点合成器:Canvas ImageData 像素高斯杂色注入算法

在数码相机、高清显示屏与现代矢量图形技术高度发达的今天,画面可以做到绝对的锐利、平滑与无瑕。然而,当一张秋日手账插画或拍立得照片过于“平整无瑕”时,往往会散发出一种冰冷生硬的“数码塑料感(Digital Plasticity&#xff0…

2026/10/12 0:00:59 阅读更多 →
活字印刷古籍线装排版:Canvas 竖排文字与栏线自适应算法

活字印刷古籍线装排版:Canvas 竖排文字与栏线自适应算法

在现代网页与移动端设计中,横排(Horizontal Layout)早已经成为了绝对的主流。然而,当我们翻开泛黄的线装古籍、宋版木刻诗集,或是欣赏一张茶道雅集的手写便签时,那种**自上而下纵向书写、自右向左逐列铺展&…

2026/10/12 0:00:59 阅读更多 →
周日晚间的“精神松绑减震器”:无压力情绪倾倒箱与温和轻声陪伴

周日晚间的“精神松绑减震器”:无压力情绪倾倒箱与温和轻声陪伴

每到周日的晚上八点到十点,很多人心里都会悄悄亮起一盏警示灯。 在心理学上,这种现象有一个专门的称谓——“周日夜晚焦虑症(Sunday Scaries)”。明天又是周一,闹钟又要重新在七点响彻卧房;脑海里仿佛有一个…

2026/10/12 0:00:59 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/12 0:16:30 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/12 0:16:38 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/12 0:16:43 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 21:05:09 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 14:48:49 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 18:11:17 阅读更多 →