一 致 的 数 据 访 问 技 术 ADO/OLE DB ( 三)
潘 爱 民
四、ADO 高 级 特 性
---- 为 了 更 全 面 地 介 绍 一 致 数 据 访 问 技 术, 这 一 部 分 将 重 点 介 绍ADO 的 一 些 高 级 特 性。 我 们 在 编 写 数 据 访 问 应 用 系 统 时, 通 常 情 况 下 只 是 简 单 地 对 数 据 进 行 各 种 增、 删、 改 的 操 作, 但 有 时 情 形 要 复 杂 一 些, 我 们 可 能 需 要 考 虑 性 能, 也 可 能 需 要 支 持 大 数 据 量, 或 者 支 持 长 数 据 类 型 等 等, 在 这 些 情 况 下, 我 们 可 以 考 虑 使 用ADO 的 一 些 高 级 特 性。 下 面 分 别 介 绍 这 些 高 级 特 性。---- (1) 处 理 数 据 定 义 语 言
---- 数 据 定 义 语 言 是 指 支 持 数 据 库 对 象 维 护 的 一 些SQL 语 句, 比 如CREATE TABLE、DROP TABLE 或ALTER TABLE 等。 因 为 执 行 这 些 语 句 并 不 产 生 记 录 集, 所 以 我 们 没 有 必 要 使 用Recordset 对 象, 只 需 使 用Command 对 象 即 可。 为 了 区 别 于 存 储 过 程(store procedure),Command 对 象 的CommandType 属 性 必 须 指 定 为adCmdText。 下 面 的 代 码 显 示 了 这 种 用 法:
Dim Cn As New ADODB.Connection Dim Cmd As New ADODB.Command 'If the ADOTestTable does not exist On Error GoTo AdoError Cn.Open "MySamples","sa" Set Cmd.ActiveConnection = Cn Cmd.CommandText = "drop table MyTestTable" Cmd.CommandType = adCmdText Cmd.Execute Cmd.CommandText = "set nocount on" Cmd.Execute Cmd.CommandText = "create table MyTestTable (id int, Name char(100))" Cmd.Execute Cmd.CommandText = "insert into MyTestTable values(1, 'Pan Aimin')" Cmd.Execute Cn.Close Exit Sub AdoError: 'handle error ......
---- (2) 使 用" 编 译 过" 的Command 对 象
---- 在 第 二 部 分 介 绍Command 命 令 对 象 时, 我 们 曾 经 说 过 可 以 通 过Prepared 属 性 指 示 底 层 的 提 供 者 为 当 前 查 询 命 令 准 备 一 个 编 译 过 的 版 本, 以 后 再 执 行 此 命 令 时, 速 度 会 大 大 加 快。 如 果 我 们 在 执 行 命 令 之 前, 把Command 对 象 的Prepared 属 性 设 置 为True, 则 命 令 被 首 次 执 行 时, 其 查 询 串 被 优 化 处 理, 以 后 再 调 用 此 查 询 命 令 时, 性 能 会 明 显 提 高, 因 为 查 询 串 已 经 被 优 化 和" 编 译 过" 了。 而 且Prepared 属 性 对 于 参 数 化 的 查 询 串 也 有 效。
---- 当 然 并 不 是 所 有 的OLE DB 提 供 者 都 支 持 这 种 特 性,SQL Server OLE DB 支 持 这 种 特 性, 当 设 置 了Prepared 属 性 为True 的 命 令 被 执 行 时, 底 层 的 提 供 者 实 际 上 创 建 了 一 个 临 时 的 存 储 过 程, 当 下 次 执 行 此 命 令 时, 提 供 者 启 动 存 储 过 程。 当 命 令 对 象 或 连 接 对 象 被 删 除 时, 此 临 时 存 储 过 程 也 被 删 除。
---- 如 果Command 对 象 只 被 执 行 一 次, 那 么 使 用Prepared 属 性 没 有 任 何 意 义, 甚 至 反 而 降 低 效 率。
---- Prepared 属 性 的 用 法 比 较 简 单, 这 里 不 再 给 出 代 码。
---- (3) 存 储 过 程 和 参 数 处 理
---- 存 储 过 程 与 前 面 介 绍 的" 编 译 过" 的 命 令 对 象 有 点 类 似, 存 储 过 程 运 行 在 服 务 器 上, 它 可 以 把 复 杂 的 应 用 逻 辑 封 装 在 服 务 器 一 端, 而 在 客 户 程 序 中 只 需 调 用 简 单 的SQL 语 句 即 可。 如 果 我 们 要 在ADO 的Command 对 象 中 执 行 存 储 过 程, 可 以 把CommandType 属 性 指 定 为adCmdStoredProc, 当 然, 在 存 储 过 程 类 型 的Command 对 象 中, 不 要 再 指 定Prepared 属 性 为True。 存 储 过 程 的 用 法 非 常 简 单, 下 面 是 一 个 例 子:
Dim Cmd As New ADODB.Command Dim rs As New ADODB.Recordset Cmd.ActiveConnection = "DSN=MySamples;uid=sa" Cmd.CommandText = "MyProc" Cmd.CommandType = adCmdStoredProc Set rs = Cmd.Execute() Debug.Print rs(0) rs.Close
---- 存 储 过 程 允 许 包 含 输 入 输 出 参 数 和 返 回 值, 对 应 到Command 对 象 中, 这 些 参 数 就 是Parameter 对 象, 输 入 参 数 的 处 理 比 较 简 单, 输 出 参 数 和 返 回 值 的 处 理 有 所 不 同, 只 有 当 返 回 的Recordset 对 象 的 记 录 全 部 遍 历 或Recordset 对 象 关 闭 之 后, 输 出 参 数 和 返 回 值 才 真 正 有 效。
---- 对 于 下 面 的 存 储 过 程:
CREATE PROCEDURE MyProc @ioparm int OUTPUT AS SELECT name FROM MyTestTable WHERE id < 2 SELECT @ioparm = 1 RETURN 100
---- 下 面 的 代 码 执 行 此 存 储 过 程:
Dim Cmd As New ADODB.Command Dim rs As New ADODB.Recordset Dim param As Parameter Cmd.ActiveConnection = "DSN=MySamples;UID=sa" Cmd.CommandText = "MyProc" Cmd.CommandType = adCmdStoredProc 'Set up parameters. Set param = Cmd.CreateParameter("Return", adInteger, adParamReturnValue, , 0) Cmd.Parameters.Append param Set param = Cmd.CreateParameter("Output", adInteger, adParamOutput, , 0) Cmd.Parameters.Append param Set rs = Cmd.Execute If Not rs.EOF And Not rs.BOF Then Debug.Print rs(0) rs.Close End If Debug.Print Cmd(0) 'The return code Debug.Print Cmd(1) 'The Output parameter
---- (4) 批 修 改 处 理
---- 我 们 知 道Recordset 对 象 的Update 方 法 用 于 修 改 当 前 记 录, 而UpdateBatch 方 法 则 用 于 递 交 所 有 对 当 前 记 录 集 的 增、 删、 改 操 作。 把Recordset 对 象 的LockType 属 性 设 置 为adLockBatchOptimistic, 则UpdateBatch 方 法 有 效, 当 然, 不 同 的OLE DB 提 供 者 可 能 还 会 有 不 同 的 要 求, 比 如,SQL Server 提 供 者 也 要 求 游 标 类 型 为 键 集 游 标 或 静 态 游 标。UpdateBatch 方 法 可 以 一 次 把 客 户 端 所 有 的 修 改 传 送 到 数 据 库 中, 相 对 应 地, 也 可 以 调 用CancelBatch 方 法 取 消 所 有 的 修 改 操 作。
---- 在 调 用 了UpdateBatch 方 法 之 后, 错 误 处 理 与 通 常 有 所 不 同。 在 错 误 处 理 过 程 中, 可 以 通 过 设 置Recordset 对 象 的Filter 属 性 为adFilterConflictingRecords, 然 后 对 冲 突 的 记 录 逐 个 进 行 处 理。 下 面 我 们 给 出 一 个 批 修 改 处 理 的 例 子:
Dim rs As New ADODB.Recordset rs.CursorLocation = adUseClient rs.CursorType = adOpenKeyset rs.LockType = adLockBatchOptimistic rs.Open "select * from MyTestTable", "DSN=MySamples;uid=sa" 'Change the type for a specified title. While (Not rs.EOF) If Trim(rs("Title")) = "instructor" Then rs("Title") = "Engineer" End If rs.MoveNext Wend rs.UpdateBatch rs.Close
---- (5) 多 记 录 集 处 理
---- 有 些 情 况 下, 一 个SQL 语 句 可 以 产 生 多 个 记 录 集, 存 储 过 程 也 可 以 产 生 多 个 记 录 集。 在 这 些 情 况 下, 我 们 可 以 逐 个 获 取 记 录 集, 利 用Recordset 对 象 的NextRecordset 方 法 可 以 依 次 获 得 每 一 个 记 录 集, 如 果 所 有 的 记 录 集 都 已 经 获 得, 则 最 后 调 用NextRecordset 方 法 返 回Nothing。
---- 下 面 的 代 码 说 明 了 这 种 用 法:
Dim cmd As New ADODB.Command Dim rs As ADODB.Recordset Cmd.ActiveConnection = "DSN=MySamples;UID=sa" Cmd.CommandText = "MyNextProc" Cmd.CommandType = adCmdStoredProc Set rs = Cmd.Execute() While Not rs Is Nothing If (Not rs.EOF) Then Debug.Print rs(0) End If Set rs = rs.NextRecordset() Wend
---- (6) 客 户 端 游 标 和 服 务 器 端 游 标
---- 游 标 服 务 是 数 据 访 问 的 重 要 内 容, 在 第 二 部 分 介 绍Recordset 对 象 时, 我 们 说 明 了 游 标 的4 种 类 型, 在Recordset 对 象 还 有 一 个 属 性CursorLocation 用 于 指 定 游 标 的 位 置, 我 们 可 以 指 定 使 用 客 户 端 的 游 标, 也 可 以 指 定 使 用 服 务 器 端 游 标。CursorLocation 属 性 的 缺 省 值 为adUseServer, 使 用 服 务 器 端 游 标 的 好 处 是, 程 序 对 数 据 库 的 修 改 可 以 立 即 反 映 到 服 务 器, 而 且, 其 他 用 户 对 数 据 库 的 操 作 也 可 以 马 上 反 映 出 来, 但 使 用 服 务 器 端 游 标 带 来 了 高 网 络 流 量, 每 一 个 数 据 访 问 都 需 要 通 过 网 络 交 换 数 据。
---- ADO 提 供 了 客 户 端 数 据 缓 存 处 理, 因 此, 在 打 开Recordset 对 象 前, 可 以 设 置CursorLocation 为adUseClient, 指 定 使 用 客 户 端 游 标。 通 过 客 户 端 游 标,ADO 利 用 本 地 数 据 缓 存 以 降 低 网 络 流 量, 虽 然 在 数 据 访 问 灵 活 性 上 有 所 损 失, 但 却 大 大 提 高 了 通 过 网 络 访 问 数 据 库 的 性 能。
---- (7) 长 数 据 类 型 处 理
---- 现 在 的 应 用 越 来 越 离 不 开 数 据 库 的 长 数 据 类 型, 长 数 据 类 型 包 括 文 本 或 二 进 制 信 息, 可 用 于 记 录 图 像、 声 音 及 其 他 多 媒 体 或 非 多 媒 体 信 息。 有 时 候 长 数 据 并 不 很 长, 可 以 直 接 通 过Field 对 象 的Value 属 性 进 行 读 取 或 赋 值; 有 时 候 长 数 据 非 常 长, 长 到 内 存 都 难 以 容 下, 这 时 可 以 通 过Field 对 象 的 块 操 作 进 行 访 问。
---- 用Field 对 象 的GetChunk 方 法 可 以 读 取 部 分 或 全 部 的 数 据, 在 内 存 使 用 有 限 制 的 情 况 下, 就 可 以 用GetChunk 读 取 部 分 数 据, 连 续 调 用GetChunk 以 读 取 所 有 的 数 据。 对 应 地, 我 们 可 以 连 续 调 用Field 对 象 的AppendChunk 方 法 对 当 前 记 录 的 某 个 长 数 据 类 型 域 赋 值。 请 注 意,GetChunk 和AppendChunk 只 对Attributes 属 性 为adFldLong 的Field 对 象 有 效。
---- 有 时 候, 在Command 对 象 的Parameters 集 合 的Parameter 对 象 中 也 需 要 使 用 长 数 据 类 型, 所 以GetChunk 和AppendChunk 方 法 对 于Parameter 对 象 同 样 适 用。
---- 下 面 给 出 一 个 使 用GetChunk 和AppendChunk 的 例 子:
Dim Cn As New ADODB.Connection Dim rsRead As New ADODB.Recordset Dim rsWrite As New ADODB.Recordset Dim strChunk As String Dim Offset As Long Dim Totalsize As Long Dim ChunkSize As Long Cn.Open "MySamples", "sa" rsRead.CursorType = adOpenStatic rsRead.Open "select My_LongData from MyTestTable", Cn rsWrite.CursorType = adOpenKeyset rsWrite.LockType = adLockBatchOptimistic rsWrite.Open "select * from MyBLOB", Cn ChunkSize = 1000 Totalsize = rsRead("My_LongData").ActualSize Do while Offset < Totalsize strChunk = rsRead("My_LongData").GetChunk(ChunkSize) Offset = Offset + ChunkSize rsWrite("Info").AppendChunk strChunk Loop rsWrite.UpdateBatch rsWrite.Close rsRead.Close
---- (8) Recordset 对 象 的 永 久 存 储 机 制
---- Recordset 对 象 的 永 久 存 储 机 制 是ADO 2.0 版 本 新 增 的 功 能, 我 们 可 以 把Recordset 对 象 保 存 到 一 个 文 件 中, 以 后 再 从 文 件 中 打 开Recordset 对 象, 并 继 续 操 作。 当 我 们 在 使 用 客 户 端 游 标 时, 可 以 使 用 该 功 能, 首 先 连 接 到 数 据 源, 并 执 行 查 询, 然 后 调 用Recordset 对 象 的Save 方 法, 把Recordset 对 象 保 存 到 文 件 中。 即 使 关 闭 了 机 器, 当 以 后 再 打 开 机 器 时, 执 行Open("filename", , ,adCmdFile), 可 以 继 续 执 行 数 据 操 作。
---- (9) 异 步 操 作 和 事 件 响 应 机 制
---- ADO 2.0 版 本 支 持 异 步 操 作, 当 连 接 数 据 源 或 进 行 记 录 集 访 问 时, 我 们 可 以 选 择 异 步 方 式。 使 用 异 步 方 式 进 行 数 据 源 连 接 时, 调 用Open 后, 函 数 马 上 返 回, 在 等 待 连 接 完 成 之 前, 我 们 可 以 继 续 执 行 其 他 的 操 作。 当 异 步 操 作 完 成 后, 我 们 可 以 接 到 操 作 完 成 的 通 知。Recordset 对 象 在 批 量 获 取 数 据 时 也 支 持 异 步 操 作( 要 求 使 用 客 户 端 游 标), 它 只 返 回 查 询 结 果 的 第 一 条 记 录, 以 后 可 以 在 后 台 继 续 获 取 后 面 的 记 录。
---- 与 异 步 操 作 相 对 应 的 是 事 件 机 制,Connection 和Recordset 对 象 提 供 了 很 多 通 知 事 件, 包 括 连 接 完 成、 命 令 将 执 行、 事 务 处 理 各 阶 段、 记 录 集 在 进 行 数 据 修 改 或 其 他 一 些 操 作 等 等,ADO 都 提 供 了 事 件 通 知。 通 过 事 件 控 制 函 数, 我 们 可 以 编 写 出 更 加 规 范 的 客 户 程 序, 提 供 更 为 灵 活 的 前 后 台 处 理 方 式。( 未 完 待 续)
---- ( 作 者 地 址: 北 京 大 学 计 算 机 科 学 技 术 研 究 所,100871, 收 稿 日 期:1999.02)
---- 责 任 编 辑: 许 菊 芳 xu_jufang@ccw.com.cn
-