ADO:101 级教程






4.47/5 (19投票s)
ActiveX 数据对象 (ADO) 的 101 级教程。
引言
ActiveX 数据对象,更常被称为 ADO,是微软世界中一种流行的数据库访问技术。 微软已经开发并推广这个编程接口很多年了,并且在 .NET 的情况下,为了适应 .NET 世界,重新设计了大部分(以及一个新的品牌 -- ADO.NET)。
尽管 ADO.NET 已经出现,但仍然存在大量用 C++ 编写的遗留 ADO 代码。 因此,那些不是从小就接触 ADO 的程序员,在未来的许多年里,仍将面临维护 C++ 中的遗留 ADO 代码的问题。
在这篇简短的文章中,我将提供 C++ 中 ADO 的一个简单介绍。这个 101 级别的教程将重点介绍以下常用的 ADO 操作:
- 初始化 COM 子系统
- 建立数据库连接
- 发出一个简单的
select
语句 - 检索记录结果
- 关闭结果和连接对象
本文的目标读者是熟悉 C++ 并且对 ActiveX 模板库 (或 ATL) 有一定了解,但很少或没有进行过 ADO 编程的人。 我在这里包含的代码可以快速地剪切和粘贴到新的 C++ 源文件中,并且我鼓励任何需要重新使用此代码的人抓取它并运行。
从 C++ 中使用 ADO 的一个潜在问题是管理 COM 对象。 如果您正确使用智能指针,那么对象将在超出范围时自动释放。 我提供的示例将演示其中的一些内容,但如果您需要更多详细信息,最好阅读像 XYZ 这样的书。
让我们开始吧!! 首先,我们必须选择要使用的 ADO 版本。
1. 导入类型库
可以导入多个 ADO 类型库,并且它们因版本而异。 可用的类型库列表将根据您安装的 Windows 操作系统和开发工具(Visual C++ 6、Visual C++ 7.x 等)而有所不同。 在我的系统中,C:\Program Files\Common Files\System\ado 子目录中,列表包括以下内容:
- msado20.tlb
- msado21.tlb
- msado25.tlb
- msado26.tlb
对于此示例,我选择了最新的类型库文件,版本 msado26.tlb。 然后,我添加了以下 import
语句
#import "c:\program files\common files\system\ado\msado26.tlb" no_namespace rename( "EOF", "A_EOF" )
2. 初始化 COM 并创建连接对象
我的示例中的第一步包括初始化 COM 并创建 ADO 连接对象的实例。
HRESULT hr = ::CoInitialize(NULL);
if (FAILED(hr))
{
return false;
}
_ConnectionPtr pConnection;
hr = pConnection.CreateInstance(__uuidof(Connection));
if (FAILED(hr))
{
return false;
}
3. 建立数据库连接
现在,我们准备打开一个数据库连接。 此操作放在 C++ 的 try
/catch
块中。 如果此操作失败,则会抛出一个 _com_error
类型的异常并立即捕获
try
{
pConnection->Open(strConnectionString,
_T(""), _T(""), adOpenUnspecified);
// ...
}
catch (_com_error&)
{
::CoUninitialize();
// ...
}
4. 构造 SQL 语句
最后,我们准备执行 SQL 语句。 我将使用最简单的示例来发出 "SELECT GETDATE()
",它返回一个单行/单列的结果。
_CommandPtr pCommand(__uuidof(Command));
pCommand->ActiveConnection = pConnection;
pCommand->CommandText = "SELECT GETDATE()";
5. 执行语句并检索结果
与 JDBC 等其他数据库抽象层不同,ADO 语句在结果集对象上执行。 要执行语句,您可以创建一个 command
对象并将其设置为或直接链接到也创建的 recordset
对象。 另一种选择是完全绕过创建 command
对象的过程,并立即执行 recordset
,在 recordset
对象上指定 SQL 语句文本。
_RecordsetPtr pRecordSet(__uuidof(Recordset));
pRecordSet->PutRefSource(pCommand);
_variant_t vNull(DISP_E_PARAMNOTFOUND, VT_ERROR);
pRecordSet->Open(vNull, vNull, adOpenDynamic,
adLockOptimistic, adCmdText);
char szTimeStamp[64] = { 0 };
if (!pRecordSet->A_EOF)
{
_Recordset **ptrResults = NULL;
pRecordSet->QueryInterface(__uuidof(_Recordset),
(void **) ptrResults);
上面的代码检索了生成的 recordset
。 但是,首先,它会在读取返回的行和列之前检查 EOF 状态。
6. 迭代结果
返回的 recordset
对象非常简单;它只包含单行单列数据。
// SELECT GETDATE() returns one row without a column name
_variant_t vField(_T(""));
_variant_t vResult;
vResult = pRecordSet->GetFields()->GetItem(vField)->Value;
_bstr_t strTimeStamp(vResult);
strncpy(szTimeStamp, (char*) strTimeStamp, 63);
if (szTimeStamp > 0)
{
char szFeedback[256] = { 0 };
sprintf(szFeedback, "SQL timestamp is: %s", szTimeStamp);
AfxMessageBox(szFeedback, MB_OK | MB_ICONINFORMATION, 0);
}
7. 释放资源
pRecordSet->Close();
pConnection->Close();
::CoUninitialize();
在这里,我们通过关闭 recordset
和连接以及释放为 COM 分配的资源来完成。