一 致 的 数 据 访 问 技 术 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
-
浙公网安备 33010602011771号