65.9K
CodeProject 正在变化。 阅读更多。
Home

通过 ADO 更新和添加数据库记录

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.16/5 (13投票s)

2007年1月31日

8分钟阅读

viewsIcon

71858

downloadIcon

2259

一篇关于通过VC++(MFC)使用OLE DB和ODBC API的文章。并附带一些比较。

引言

这篇文章将带您进入数据库的神秘世界,探索OLE DB和ODBC的一些基本概念。我为什么写这篇文章?…… 仅仅是为了与程序员分享知识,同时,在我开发第一个集成数据库的C++应用程序时,我遇到了许多与数据库连接及其工作相关的难题。因此,我希望阅读完本文后,程序员能够对C++中的数据库风险有一个初步的认识和概念。您将在这里找到OLE DB和ODBC两个API的介绍以及一些代码片段。

背景

本文不需要任何特殊的背景知识,您只需要了解数据库概念,并熟悉VC++和MFC环境。如果您能进一步深入学习它们会更好。

https://codeproject.org.cn/database/connectionstrings.asp

https://codeproject.org.cn/database/oledbconsumer1.asp

使用代码

当您通读全文后,会发现它非常精妙且概念性强。我希望那时您能轻松理解代码片段及其用法。为了方便您,我附上了一些可用于操作数据库的代码和算法,以及本文的PPT格式。您可以下载并尽情享用。

VC++中访问数据库的技术

VC++在您的应用程序中使用和访问数据库提供了以下技术:
1) 数据访问对象 (DAO)
2) 开放数据库连接器 (ODBC)
3) OLE(对象链接与嵌入)数据库
4) ActiveX 数据对象 (ADO)
我们将详细介绍ADO,并讨论一些ODBC和OLE DB。

OLE DB & ODBC

OLE DB 和 ODBC 是为了提供对广泛数据源的访问而设计的API。

ODBC 主要旨在跨平台环境中提供对SQL数据的访问。

OLE DB 包含了ODBC中定义的SQL功能,但也定义了适用于访问非SQL数据的接口。

ODBC是为了访问关系数据库而创建的。

OLE DB 是为关系型和非关系型信息源设计的,例如Web的文本和图形数据。
任何可能包含数据的内容都可以通过OLE DB技术进行访问。

什么是ADO?

ADO被设计为OLE DB之上的另一层,专门用于数据库访问。
Web浏览器用户可以在客户端缓存整套数据记录。ADO控件随Microsoft的Internet Explorer Web浏览器(4.0及以上版本)分发。

ADO对象

1-基本对象包括:
2-Connection
3-Error
4-Command
5-Parameter
6-Recordset
7-Field

连接对象

->用于建立和维护与数据库的连接。
->建立连接
->在打开连接之前,使用数据库位置、用户ID和密码等连接信息配置对象。
->调用其Open方法打开连接。
->调用其Close方法关闭连接。
->通过Connection对象控制所有高级连接功能。
->这包括通过Connection对象的BeginTrans、CommitTrans和RollbackTrans方法进行所有事务控制。

Error Object (错误对象)

->每当发生数据库错误时,数据库的错误信息将被放入ADO Error对象中。
->错误对象中的信息是数据库的错误信息,而不是ADO的错误信息。

命令对象

->用于在数据库中执行命令。
->此对象可用于运行SQL语句或调用存储过程(存储在数据库中的SQL函数)。

参数对象

. ->用于传递变量以及调用存储过程或参数化查询。
->附加到Command对象以供调用命令使用。

Recordset Object (记录集对象)

->此对象包含数据库中的一组记录。
->记录集是发送到数据库的命令的结果,该命令返回一组记录。

Field Object (字段对象)

它代表Recordset中的单个列。->Field对象始终包含Variant数据值。
->使用ADO对象的程序员必须将Variant的值转换为所需的数据类型,并在更新值时将其转换回Variant。

使用ADO ActiveX控件

->在Visual C++应用程序中有两种不同的方法来使用ADO控件。
->通过使用ActiveX控件将ADO集成到应用程序中的简单方法是:
->将ADO ActiveX控件添加到项目中。

Sample image

->指定数据库连接。

Sample image

->指定记录源。

Sample image

->将ADO控件指定为控件的数据源。

Sample image

->运行中的ADO控件数据库应用程序。

Sample image

使用这种方法在构建ADO应用程序时涉及大量的非必要开销。对于您想要提取的每个SQL查询或表,都需要添加一个单独的ADO控件。每个ADO控件都会建立一个单独的数据库连接,这可能会给连接数有限制的数据库带来问题。
并非所有数据绑定控件都支持ADO

导入ADO DLL

->MFC类层次结构中没有可以直接用于ADO的类。
->Microsoft提供了其他方法,通过使用一个新的C++预编译器指令#import来为ADO中的每个对象创建和使用类。
这个#import指令告诉Visual C++编译器导入指定的DLL,创建自动包含在项目中的头文件。这些头文件的文件扩展名是.TLH 和 .TLI。这两个文件包含DLL中可用于代码的每个对象的类定义。#import指令消除了在项目中包含DLL的.LIB文件的需要。通过在定义数据库对象的头文件开头放置以下代码来导入ADO DLL:
#define INITGUID 
#import "C:\Program Files\CommonFiles\System\ADO\msado15.dll"     rename_namespace("ADOCG")      
rename("EOF", "EndOfFile") 
   using namespace ADOCG; 
   #include "icrsint.h" 

连接数据库

COM是ActiveX控件和OLE的基础“对象模型”。在使用任何ADO对象之前,必须初始化应用程序的COM环境,以调用ActiveX对象:::CoInitialize(NULL);
在完成所有ADO活动后,必须通过调用CoUninitialize函数来关闭COM环境,如下所示:
CoUninitialize();
此函数清理COM环境并为应用程序关闭做准备。

创建数据库连接

Declare a Connection object pointer, _ConnectionPtr 

_ConnectionPtr pConn; pConn.CreateInstance(__uuidof(Connection)); 

pConn->Open(L"Provider=MSDASQL.1;DataSource=TYVCDB", L"", L"",adOpenUnspecified); 

执行命令并检索数据

声明一个Command对象指针,_CommandPtr,然后使用Command对象的UUID创建一个实例,如下所示:
_CommandPtr pCmd;
pCmd.CreateInstance(__uuidof(Command)); 
pCmd->ActiveConnection = pConn; 
接下来,通过设置Command对象的CommandText属性来指定要执行的SQL命令,如下所示:
pCmd->CommandText = "Select * from Addresses"; 
此时,有两种选择可以执行此命令并检索记录。
1)     _RecordsetPtr pRs; 
    pRs = pCmd->Execute(); 

2)    _RecordsetPtr pRs; pRs.CreateInstance(__uuidof(Recordset));
     pRs->PutRefSource(pCmd); 


// Create the variant NULL 
_variant_t vNull; 
vNull.vt = VT_ERROR;
vNull.scode = DISP_E_PARAMNOTFOUND;
// Open the recordset 
pRs->Open(vNull, vNull,    adOpenDynamic,adLockOptimistic,    adCmdUnknown); 

另一种方法可以用很少的代码完成前面所有的任务:

完全跳过Command和Connection对象的使用,将所有必要的连接信息放在Recordset的Open函数中。
_RecordsetPtr pRs;
 pRs.CreateInstance(__uuidof(Recordset));
pRs->Open(_T("Provider=MSDASQL.1;Data Source=TYVCDB"), _T("select * from Addresses"),  
adOpenDynamic, adLockOptimistic, adCmdUnknown); 

导航Recordset

需要导航记录集的函数:
MoveFirst, MoveLast, MovePrevious, 和 MoveNext
Recordset对象还有两个属性:BOF 和 EOF。

访问字段值

ADO Recordset的字段中检索到的所有数据元素都是variant值。
它们需要被转换为所需使用的数据类型。
有两种方法可以做到这一点。
1)Retreive values into a variant and then convert them
    _variant_t vName; 
    CString strName; 
    vName = pRs-> GetCollect(_variant_t("Name"));
    vName.ChangeType(VT_BSTR); strName = vName.bstrVal; 
2) Microsoft创建了一系列宏来执行转换并维护记录集中的变量。为此,请定义一个新类作为记录集的接口。此类将继承自CADORecordBinding类,该类定义在icrsint.h头文件中。
class CCustomRs : public CADORecordBinding { 
    BEGIN_ADO_BINDING(CCustomRs) 
     ADO_FIXED_LENGTH_ENTRY (1, adInteger, m_ID, IDStatus,     FALSE)
     ADO_VARIABLE_LENGTH_ENTRY2(2, adVarChar, m_Name, sizeof(m_Name), NameStatus, TRUE)  
ADO_FIXED_LENGTH_ENTRY(3, adDate, m_Birthdate, BirthdateStatus, TRUE) 
    END_ADO_BINDING() 
  public: 
        LONG m_ID; 
    ULONG IDStatus; 
        CHAR m_Name[20]; 
        ULONG NameStatus; 
        DATE m_Birthdate; 
        ULONG BirthdateStatus;  
  }; 
声明该类的一个变量。接下来,创建IADORecordBinding接口的指针:CCustomRs m_RecSet; IADORecordBinding *Rs = NULL; IADORecordBinding接口将Recordset对象的字段绑定到C/C++变量。每当绑定的Recordset的当前行发生变化时,所有绑定的字段都会被复制到变量中。

IADORecordBinding接口方法

1)BindToRecordset
2)AddNew
3)Update
一旦记录集被检索,就检索IADORecordBinding接口的指针,并将自定义记录集类绑定到Recordset对象,如下面的代码所示:
if (FAILED(pRs->QueryInterface(__uuidof(IADORecordBinding), (LPVOID *)&Rs)))
    _com_issue_error(E_NOINTERFACE); Rs->BindToRecordset(&m_RecSet); 

BEGIN_ADO_BINDING宏设置了与其余宏一起创建的结构定义。

宏集由END_ADO_BINDING宏关闭。

ADO_FIXED_LENGTH_ENTRY
它用于大小固定的任何数据库字段。
ADO_NUMERIC_ENTRY Macros 宏只用于数字字段。
ADO_VARIABLE_LENGTH_ENTRY Macros 使用此系列宏处理长度可能变化的数据库字段。

更新记录

如果您手动检索每个字段并将其从variant转换,则需要更新已更改的每个单独字段。_variant_t vName, vValue; vName.SetString("Name"); vValue.SetString("Saqib"); pRs->Update(vName, vValue); 如果您创建了记录类并将其绑定到记录集,更新记录会稍微简单一些。Rs->Update(&m_RecSet);

添加和删除

要删除当前记录,请调用Recordset对象的Delete方法。pRs->Delete(adAffectCurrent); pRs->MovePrevious();

添加新记录

您不能直接开始向字段输入数据值。为了让用户立即输入新记录的各种数据元素,请将记录类中的值清空,并将Recordset类变量作为唯一参数传递给AddNew方法。通过记录绑定接口指针调用它,如下例所示:
CString strBlank = " ";
COleDateTime dtBlank;
m_RecSet.m_ID = 0; strcpy(m_RecSet.m_Name, (LPCTSTR)strBlank);
m_RecSet.m_dtBirthdate =  (DATE)dtBlank;
Rs->AddNew(&m_RecSet); 

关闭Recordset和Connection对象

完成记录集工作后,调用pRs->Close();。完成应用程序所有数据库交互后,通过调用Connection对象的Close方法关闭数据库连接:pConn->Close();

关注点

始终尝试为内置的MFC、STL、ATL类编写包装类,以便以最佳方式利用它们。本文在我的朋友GOLD的帮助下完成……我从他那里学到了帮助他人变得更好。

历史

请及时更新您在这里所做的任何更改或改进。如果您有任何疑问,请告知我。谢谢。

© . All rights reserved.