访问|数据
服务器组件一般用于实现三层应用程序的业务规则,因此需要与数据库交互。C++访问数据有两种方式: ADO和OLE DB消费者模板。第1 2章我们已经讨论了通用数据访问(UDA)、ADO和OLE DB的作用。
从C++ ADO与从ASP或Visual Basic访问ADO非常相似。使用完全一样的ADO COM对象(如ADODB.Recordset),只是用不同的语言创建和使用ADO COM对象。但使用ADO时没有Visual C++向导,必须编写所有的实现代码。这不仅须手工编写,而且还容易引入代码错误,因此应认真仔细。但是可从C++中得到完全可用的ADO对象模型。
OLE DB消费者使用OLE DB提供者的服务访问数据。ADO是OLE DB消费者的一个例子。使用C++可创建OLE DB消费者,完全跳过ADO层。Visual C++向导可用于创建和使用OLEDB消费者。可以使用向导指向想要访问的数据库和表格,自动生成所需的代码。但是,因为消费者代码直接指向指定的数据库,所以不如使用ADO灵活。
18.3.1 通过C++使用ADO
通过C++使用ADO需要两种技能:理解ADO对象模型和在C++中使用COM对象。我们对ADO对象模型已非常熟悉,但现在没有C++类库或模板库支持ADO。因为ADO在COM上构造,使用ADO与使用其他COM对象相同。下面介绍一下使用步骤。
1. 设置使用ADO
C++中使用COM组件最简单的方法是用# import关键字导入组件的类型库。# import 指令围绕ADO类型库产生瘦C++类,可以用接近于VBScript和Visual Basic的语句操作ADO对象和接口。大部分ADO例子用VBScript编写,用#import指令能很容易地将它们转换为C++,比在没有包装类(wrapper class)的情况下使用ADO容易。
使用#import时,可以使用no_namespace属性,这样ADO类型库内容就没有范围限制。但是,这会引起名称与EOF的冲突,而rename属性可解决这个问题。在TableStorage2.cpp增加下列内容可导入ADO类型库。
上述代码生成一个msADO15.tlh文件和一个msADO15.tli文件。msADO15.tlh文件包含前向引用、smart指针声明和typeinfo声明。msADO15.tli文件包含编译器产生的几个成员函数的实现。这两个文件用于观察和显示类型库中的可用内容。例如,下面是msADO15.tlh文件对ADO记录集的smart指针的声明:
_COM_SMARTPTR_TYPEDEF(_Recordset,_uuideof(_Recordset));
后面的例子将说明如何在代码中使用smart指针。在msADO15.tlh中还有一些枚举量的声明,使得使用ADO更为方便。下面是光标位置的枚举量声明:
如上所述,#import使得ADO的使用更为轻松,因为这个指令可自动创建包装类和来自类型库的类型信息。但对C++程序员来说,将ADO返回的VA R I A N T数据类型转变为C/C++数据类型还是比较麻烦。ADO 2.0和Visual C++ 6.0用附加的ADO VC++扩展解决了这个问题。这一章不讨论ADO VC++扩展,有关内容可参考Visual C++文献。
还增加了一个宏,这个宏可使程序容易理解,减少了嵌套代码的数量:
通过导入类型库,已可使用所有ADO对象。下面介绍如何在C++中使用ADO进行读写。我们将以图1 8 - 1 0所示的数据库为例。
假设对这个数据库已建立了一个称为MusicianDSN的ODBC连接。
2. 读数据库
首先在ITableStorage2中增加Read方法。这个方法从数据库读记录,并将这些记录插入STL数据结构中,它的参数是DSN名称、用户名和口令。在对话框中,参数在Implementation文本框中出现,如图1 8 - 11所示。
下面看一下这个方法的实现:
首先声明方法中使用的变量,注意如何使用ATL smart指针的创建ADO Connection和Recordset对象。为在代码中跟踪错误,我们采用了异常处理,因此开启一个try块。任何ADO调用都用前面声明的SAFE_CALL宏包装,如果任一方法调用返回一个表明失败的值,就抛出一个异常:
首先打开到数据库的连接,这个连接用于Recordset对象的Open方法。
如果成功,可对数据库的记录进行遍历。首先必须确保内存中当前无数据:
现在,将数据库中的数据拷贝到内存中的STL数据结构内,遍历记录直到遇到文件结束标志EOF。只在字段真的有值时才插入它,这样可以节约内存:
到目前为止,所有一切都在try 块中。如果有错误出现,程序流将进入catch 块。_com_error变量包含了错误消息的细节,我们添加了一个辅助函数将错误的详细数据格式化为字符串。我们将这个错误字符串传送到Error中,这样,在客户端可以通过Error对象的Description属性得到它。
上面讲述了如何从数据库中读数据,下面讨论如何将数据写入数据库。
3. 写数据库
在ITableStorage2中添加下列方法。它将S T L数据结构中的信息写入数据库中。操作界面如图1 8 - 1 2所示。
另外一个差异是C++不能很好地支持Variant类型。上面的例子的代码看上去不错,但它仅由字符型数据组成。如果数据类型混用的话,代码将变得混乱。下面介绍的OLE DB者消费模板将使代码变得简洁。
18.3.2 OLE DB消费者模板
OLE DB消费者模板是一个相对新的方法,其目的是改善在C++中进行数据存取的性能。它在Visual C++ 6.0 中作为ATL 3.0的一部分引入使用库扩展了C++功能,OLE DB消费者模板库是为了通过OLE DB进行数据存取而设计的。这个库能够提供:
? 访问OLE DB特征。
? 集成ATL和MFC。
? 数据库参数和列的绑定模型。
? 使用原有的C/C++数据类型进行OLE DB编程。
另外,Visual C++的AppWizard可生成基本的OLE DB消费者代码。
1. 创建OLE DB 消费者
在创建OLE DB消费者前必须建立数据源,我们采用与ADO例子中所用的数据库相似的Access数据库。用在ATL Object Wizard添加一个新的ATL对象,选择Data Access类别并选择Consumer,如图1 8 - 1 3所示。
点击Next,选择所需的OLE DB 提供者,因为我们选用了ODBC驱动程序,所以选择Microsoft OLE DB Provider for ODBC Drivers。如图1 8 - 1 4所示。
选择所用的数据源名称为Musician DSN。用户名和口令没有输入,如果数据库需要用户名和口令,可在此对话框中输入,如图1 8 - 1 5所示。
点击OK按纽,然后选择所需的数据库表,如图18 - 1 6所示。
再点击OK,向导将自动选取一些名称,根据自己的需要可进行修改。如果将要添加记录,可在Support部分选择insert框,如图1 8 - 1 7所示。
点击OK后,向导产生两个类: CMusiciansAccessor和CMusicians,可完成数据类型转换等工作。我们将只直接使用CMusicians类,下一节再做说明。
2. 读数据库
下面在ITableStorage2中添加使用新的OLE DB消费者类读数据库的方法,由ATL ObjectWizard生成的Read2不带任何参数,因为ATL Object Wizard已经将数据源名称、用户名和口令(如果你提供了)硬编码进CMusicians类,如图1 8 - 1 8所示。
OLE DB消费者模板版本的Read2方法的代码比ADO版本的代码更为简单:
如果上述代码没有编写的话,应在TableStorage2.cpp文件的顶部包含Musicians.h文件。
大部分工作已由theMusicians对象完成。当调用Open后,就创建了与数据库的连接,而且打开了Musicians表。下面的代码建立数据结构,列名称映射由手工创建:
下面遍历数据库记录。在使用OLE DB 消费者模板时,要判定记录的结束,应检查MoveNext的返回值而不是检查EOF属性。当调用MoveNext时,theMusicians对象的成员变量自动填充,在读下一个记录前,必须先调用ClearRecord,否则当该记录的字段为NULL时,前一记录的数据仍将占据成员变量。
上述代码比ADO例子中的相应代码简洁得多。下面介绍写记录。
3. 写数据库
在ITableStorage2中再增加一个称为Write2的新方法,数据库配置信息已经在CMusicians类中硬编码,因此不必向Write2传送任何参数,如图1 8 - 1 9所示。
代码的开始与Read2相似,大部分工作由theMusicians对象完成。首先调用Open以连接数据库和表。为了在数据库中添加一个记录,先填充成员变量,然后调用Insert。对一个新记录来说,应首先调用ClearRecord,确保没有前一记录的数据遗留下来。
上面使用了称为C o p y Va l u e的辅助方法。查找记录中的字段的值是重复性的工作,可用此方法减少代码的数量:
18.3.3 使用ADO,还是使用OLE DB消费者模板
进行数据存取时,可选用ADO或OLE DB消费者模板。选用ADO可重新利用你对ADO对象模型的知识,代码修改也较容易,灵活性好。但ADO不能很好地支持绑定到C++的数据类型,并且Visual C++中没有相应的库和AppWizard支持。
选用OLE DB消费者模板可从Visual C++得到库和AppWizard支持。这意味着, VisualC++提供项目的基本结构,代码编写的速度较快,出现的错误也较少,但必须学习新的用于访问数据的API。如果你要编写许多需要访问数据的组件,那么这是值得的。
本章创建了一个简单的C++服务器组件并增强其功能。我们介绍了如何访问ASP内置对象,如何访问各种COM+接口。描述了数据存取的两种方法。C++中可使用ADO对象模型,但需要进行大量的数据类型转换。ATL Object Wizard可创建OLE DB消费者,生成的代码比ADO简洁,但灵活性较差。