1、 概述
1、1 Visual C++开发数据库技术的特点
Visual C++提供了多种多样的数据库访问技术——ODBC API、MFC ODBC、DAO、OLE DB、ADO等。这些技术各有自己的特点,它们提供了简单、灵活、访问速度快、可扩展性好的开发技术。
简单性
Visual C++中提供了MFC类库、ATL模板类以及AppWizard、ClassWizard等一系列的Wizard工具用于帮助用户快速的建立自己的应用程序,大大简化了应用程序的设计。使用这些技术,可以使开发者编写很少的代码或不需编写代码就可以开发一个数据库应用程序。
灵活性
Visual C++提供的开发环境可以使开发者根据自己的需要设计应用程序的界面和功能,而且,Visual C++提供了丰富的类库和方法,可以使开发者根据自己的应用特点进行选择。
访问速度快
为了解决ODBC开发的数据库应用程序访问数据库的速度慢的问题,Visual C++提供了新的访问技术——OLE DB和ADO,OLE DB和ADO都是基于COM接口的技术,使用这种技术可以直接对数据库的驱动程序进行访问,这大大提供了访问速度。
可扩展性
Visual C++提供了OLE技术和ActiveX技术,这种技术可以增强应用程序的能力。使用OLE技术和ActiveX技术可以使开发者利用Visual C++中提供的各种组件、控件以及第三方开发者提供的组件来创建自己的程序,从而实现应用程序的组件化。使用这种技术可以使应用程序具有良好的可扩展性。
访问不同种类数据源
传统的ODBC技术只能访问关系型数据库,在Visual C++中,提供了OLE DB访问技术,不仅可以访问关系型数据库,还可以访问非关系型数据库。
1、2 Visual C++开发数据库技术
Visual C++提供了多种访问数据库的技术,如下所示:
ODBC(Open DataBase Connectivity)
MFC ODBC(Microsoft Foundation Classes ODBC)
DAO (Data Access Object)
OLE DB(Object Link and Embedding DataBase)
ADO(ActiveX Data Object)
这些技术各有自己的特点,总结如下:
ODBC
ODBC是客户应用程序访问关系数据库时提供的一个统一的接口,对于不同的数据库,ODBC提供了一套统一的API,使应用程序可以应用所提供的API来访问任何提供了ODBC驱动程序的数据库。而且,ODBC已经成为一种标准,所以,目前所有的关系数据库都提供了ODBC驱动程序,这使ODBC的应用非常广泛,基本上可用于所有的关系数据库。
但由于ODBC只能用于关系数据库,使得利用ODBC很难访问对象数据库及其它非关系数据库。
由于ODBC是一种底层的访问技术,因些,ODBC API可以使客户应用程序能够从底层设置和控制数据库,完成一些高层数据库技术无法完成的功能。
MFC ODBC
由于直接使用ODBC API编写应用程序要编制大量代码,在Visual C++中提供了MFC ODBC类,封装了ODBC API,这使得利用MFC来创建ODBC的应用程序非常简便。
DAO
DAO提供了一种通过程序代码创建和操纵数据库的机制。多个DAO构成一个体系结构,在这个结构中,各个DAO对象协同工作。MFC DAO是微软提供的用于访问Microsoft Jet数据库文件(*.mdb)的强有力的数据库开发工具,它通过DAO的封装,向程序员提供了DAO丰富的操作数据库手段。
OLE DB
OLE DB是Visual C++开发数据库应用中提供的新技术,它基于COM接口。因此,OLE DB对所有的文件系统包括关系数据库和非关系数据库都提供了统一的接口。这些特性使得OLE DB技术比传统的数据库访问技术更加优越。
与ODBC技术相似,OLE DB属于数据库访问技术中的底层接口。
直接使用OLE DB来设计数据库应用程序需要大量的代码。在VC中提供了ATL模板,用于设计OLE DB数据应用程序和数据提供程序。
ADO
ADO技术是基于OLE DB的访问接口,它继承了OLE DB技术的优点,并且,ADO对OLE DB的接口作了封装,定义了ADO对象,使程序开发得到简化,ADO技术属于数据库访问的高层接口。
2、 使用ODBC API
Microsoft 开放数据库互连(ODBC,Open DataBase Connectivity)是Microsoft Windows 开放服务体系(WOSA)的一部分,是一个数据库访问的标准接口。使用这一标准接口,我们可以不关心具体的数据库管理系统(DBMS)的细节,而只要有相应类型数据库的ODBC驱动程序,就可以实现对数据库的访问。
ODBC编程接口为我们提供了极大的灵活性,我们可以通过这一个接口访问不同种类的数据库。而且,通过相应的ODBC驱动程序,我们可以方便地实现不同数据类型之间的转换。
2.1 ODBC API 概述
ODBC是一个应用广泛的数据库访问应用编程接口(API),使用标准的SQL(结构化查询语言)作为其数据库访问语言。
2.11体系结构
ODBC的结构是建立在客户机/服务器体系结构之上,它包含如下四个部分:
应用程序(Application ):
应用程序即用户的应用,它负责用户与用户接口之间的交互操作,以及调用ODBC函数以给出SQL请求并提取结果以及进行错误处理。
ODBC驱动程序管理器(Driver Manager):
ODBC驱动程序管理器为应用程序加载和调用驱动程序,它可以同时管理多个应用程序和多个驱动程序。它的功能是通过间接调用函数和使用动态链接库(DLL)来实现的,因此它一般包含在扩展名为”DLL”的文件中。
ODBC驱动程序(Driver)
ODBC 驱动程序执行ODBC函数调用,呈送 SQL 请求给指定的数据源,并将结果返回给应用程序。驱动程序也负责与任何访问数据源的必要软件层进行交互作用,这种层包括与底层网络或文件系统接口的软件。
数据源
数据源由数据集和与其相关联的环境组成,包括操作系统、DBMS 和网络(如果存在的话)。ODBC 通过引入“数据源”的概念解决了网络拓扑结构和主机的大范围差异问题,这样,用户看到的是数据源的名称而不必关心其它东西。
2.12数据类型
ODBC使用两类数据类型:SQL数据类型和C数据类型。SQL数据类型用于数据源,C数据类型用于应用程序代码中。
2.13句柄
ODBC API 实现数据库操作的手段是语句,这是一个强有力的手段。ODBC语句除了能执行SQL语句和完成查询操作之外,还能实现大多数数据库操作。
在ODBC中,使用不同的句柄(HANDLE)来标志环境(ENVIRONMENT)、连接(CONNECTION)、语句(STATEMENT)、描述器(DESCRIPTOR)等。
句柄就是一个应用程序变量,系统用它来存储关于应用程序的上下文信息和应用程序所用到的一些对象。它和 Windows 编程中的概念类似,不过ODBC 更加完善了句柄的作用。
1、 环境句柄是 ODBC 中整个上下文的句柄,使用 ODBC 的每个程序从创建环境句柄开始,以释放环境句柄结束。所有其它的句柄(这一应用程序所有的联接句柄和语句句柄)都由环境句柄中的上下文来管理。环境句柄在每个应用程序中只能创建一个。
2、联接句柄管理有关联接的所有信息。联接句柄可以分配多个,这不仅合法而且很有用;但不要生成不必要的句柄以免资源的浪费。但是,不同的驱动程序支持的联接情况有所不同,有的驱动程序在一个应用程序中仅支持一个联接句柄,有的驱动程序仅支持一个语句句柄。在应用程序中,可以在任何适当的时候联接或脱离数据源,但不要轻易地建立或脱离联接。
3、语句句柄是 ODBC API 真正发挥重要作用的,它被用来处理 SQL 语句及目录函数,每个语句句柄只与一个联接有关。当驱动程序接收一个来自应用程序的函数调用指令而该指令包含一个语句句柄时,驱动程序管理器将使用存储在语句句柄中的联接句柄来将这一函数调用发送给合适的驱动程序。
4、描述器句柄是元数据的集合,这些元数据描述了SQL语句的参数、记录集的列等信息。当有语句被分配内存之后,描述器自动生成,称为自动分配描述器。在程序中,应用程序也可调用SQLAllocHandle分配描述器。
当应用程序调用API函数SQLAllocHandle时,驱动管理器或者ODBC驱动程序将为所声明的句柄类型分配内部结构,返回句柄值。
2.14异常处理
为了在程序开发过程中调试程序,发现程序错误,ODBC API通过两种方式返回有关ODBC API函数执行的的信息:返回码和诊断记录。返回码返回函数执行的返回值,说明函数执行成功与否。诊断记录说明函数执行的详细信息。
返回码(Return Code)
每一个ODBC API函数都返回一个代码——返回码,指示函数执行的成功与否。如果函数调用成功,返回码为SQL_SUCCESS或SQL_SUCCESS_WITH_INFO。SQL_SUCCESS指示可通过诊断记录获取有关操作的详细信息,SQL_SUCCESS_WITH_INFO指示应用程序执行结果带有警告信息,可通过诊断记录获取详细的信息。如果函数调用失败,返回码为SQL_ERROR。
下面的一段代码根据函数SQLFetch()执行的返回码,判断函数执行的成功与否,从而据此进行相应的处理。
SQLRETURN rtcode;
SQLHSTMT hstmt;
While(rtcode=SQLFetch(hstmt)!=SQL_NO_DATA)
{
if(rtcode==SQL_SUCCESS_WITH_INFO)
{
//显示警告信息
}
else
{
//显示出错信息
break;
}
//函数调用成功,进行处理
}
如果程序执行错误,返回码为SQL_INVALID_HANDLE,程序无法执行,而其它的返回码都带有程序执行的信息。
诊断记录(Diagnostic Records)
每个ODBC API函数都能够产生一系列的反映操作信息的诊断记录。这些诊断记录放在相关连的ODBC句柄中,直到下一个使用同一个句柄的函数调用,该诊断记录一直存在。诊断记录的大小没有限制。
诊断记录有两类:头记录(Head Record)和状态记录(Status Record)。头记录是第一版权法记录(Record 0),后面的记录为状态记录。诊断记录有许多的域组成,这些域在头记录和状态记录中是不同的。
可以用SQLGetDiagField函数获取诊断记录中的特定的域,另外,可以使用SQLGetDiagRec()获取诊断记录中一些常用的域,如SQLSTATE、原始错误号等。
头记录
头记录的各个域中包含了一个函数执行的通用信息,无论函数执行成功与否,只要不返回SQL_INVALID_HANDLE,都会生成头记录。
状态记录
状态记录中的每个域包含了驱动管理器、ODBC驱动程序或数据源返回的特定的错误或警告信息,包括SQLSTATE、原始错误码、诊断信息、列号和行号等。只有函数执行返回SQL_ERROR,SQL_STILL_EXEUTING、SQL_SUCCESS_WITH_INFO、SQL_NEED_DATA或SQL_NO_DATA时,才会生成诊断记录。
使用SQLGetDiagRec和SQLGetDiagField
应用程序可以调用函数SQLGetDiagRec或SQLGetDiagField获取诊断信息。对于给定的句柄,这两个函数返回最近使用该句柄的函数的诊断信息。当有使用该句柄的函数执行时,句柄记录所记录的原有的诊断信息被覆盖。如果函数执行后产生多个状态记录,程序必须多次调用这两个函数以获取信息
(二)虽然直接应用ODBC API编制应用程序相对来说较为繁琐,但是,由于直接使用ODBC API编写的程序相对要简洁、高效
2.2 应用ODBC API建立应用程序
虽然直接应用ODBC API编制应用程序相对来说较为繁琐,但是,由于直接使用ODBC API编写的程序相对要简洁、高效。所以,我们有必要学习直接使用ODBC API编程。
看图之王ACDSee 6.0应用大全 | 数码人像照片矢量化及艺术处理 | |
六天学会Visual Basic数据库编程 | 解析“震荡波”恶性蠕虫病毒 |
一般地,编写ODBC程序主要有以下几个步骤:
分配ODBC环境
分配连接句柄
连接数据源
构造和执行SQL语句
取得执行结果
断开同数据源的连接
释放ODBC环境
2.21 分配ODBC环境
对于任何ODBC应用程序来说,第一步的工作是装载驱动程序管理器,然后初始化ODBC环境,分配环境句柄。
首先,程序中声明一个SQLHENV类型的变量,然后调用函数SQLAllocHandle,向其中传递分配的上述SQLHENV类型的变量地址和SQL_HANDLE_ENV选项。如下代码所示:
SQLHENV henv;
SQLAllocHandle(SQL_HANDLE_ENV,SQL_NULL_HANDLE,&henv);
执行该调用语句后,驱动程序分配一个结构,该结构中存放环境信息,然后返回对应于该环境的环境句柄。
2.22分配连接句柄
分配环境句柄后,在建立至数据源的连接之前,我们必须分配一个连接句柄,每一个到数据源的连接对应于一个连接句柄。
首先,程序定义了一个SQLHDBC类型的变量,用于存放连接句柄,然后调用SQLAllocHandle函数分配句柄。如下代码所示:
SQLHDBC hdbc;
SQLAllocHandle(SQL_HANDLE_DBC,henv,&hdbc);
henv为环境句柄。
2.23 连接数据源
当连接句柄分配完成后,我们可以设置连接属性,所有的连接属性都有缺省值,但是我们可以通过调用函数SQLSetConnectAttr()来设置连接属性。用函数SQLGetConnectAttr()获取这些连接属性。
函数格式如下:
SQLRETURN SQLSetConnectAttr(SQLHDBC ConnectionHandle,SQLINTEGER Attribute,SQLPOINTER ValuePtr,SQLINTEGER StringLength);
SQLRETURN SQLGetConnectAttr(SQLHDBC ConnectionHandle,SQLINTEGER Attribute,SQLPOINTER ValuePtr,SQLINTEGER StringLength);
应用程序可以根据自己的需要设置不同的连接属性。
完成对连接属性的设置之后,就可以建立到数据源的连接了。对于不同的程序和用户接口,可以用不同的函数建立连接:SQLConnect、SQLDriverConnect、SQLBrowseConnect。
SQLConnect
该函数提供了最为直接的程序控制方式,我们只要提供数据源名称、用户ID和口令,就可以进行连接了。
函数格式:
SQLRETURN SQLConnect(SQLHDBC ConnectionHandle,SQLCHAR ServerName,SQLSMALLINT NameLength1,SQLCHAR UserName,SQLSMALLINT NameLength2,SQLCHAR *Authentication,SQLSMALLINT NameLength3);
参数:
ConnectionHandle 连接句柄
ServerName 数据源名称
NameLength1 数据源名称长度
UserName 用户ID
NameLength2 用户ID长度
Authentication 用户口令
NameLength3 用户口令长度
返回值:
SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_ERROR, or SQL_INVALID_HANDLE.
成功返回SQL_SUCCESS,如果返回值为SQL_ERROR或SQL_SUCCESS_WITH_INFO,可以用函数SQLGetDiagRec获取相应SQLSTATE的值。
下面的代码演示了如何使用ODBC API的SQLConnect函数建立同数据源SQLServer的连接。
#include “sqlext.h” SQLHENV henv;; SQLHDBC hdbc; SQLHSTMT hstmt; SQLRETURN retcode; /*Allocate environment handle */ retcode = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv); if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) { /* Set the ODBC version environment attribute */ retcode = SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0); if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) { /* Allocate connection handle */ retcode = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc); if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) { /* Set login timeout to 5 seconds. */ SQLSetConnectAttr(hdbc, (void*)SQL_LOGIN_TIMEOUT, 5, 0); /* Connect to data source */ retcode = SQLConnect(hdbc, (SQLCHAR*) "Sales", SQL_NTS, (SQLCHAR*) "JohnS", SQL_NTS, (SQLCHAR*) "Sesame", SQL_NTS); if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO){ /* Allocate statement handle */ retcode = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt); if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) { /* Process data */; SQLFreeHandle(SQL_HANDLE_STMT, hstmt); } SQLDisconnect(hdbc); } SQLFreeHandle(SQL_HANDLE_DBC, hdbc); } } SQLFreeHandle(SQL_HANDLE_ENV, henv); SQLDriveConnect |
函数SQLDriveConnect用一个连接字符串建立至数据源的连接。它可以提供比SQLConnect函数的三个参数更多的信息,可以让用户输入必要的连接信息。
如果连接建立,该函数返回完整的字符串,应用程序可使用该连接字符串建立另外的连接。
函数格式:
SQLRETURN SQLDriverConnect(SQLHDBC ConnectionHandle,SQLHWND WindowHandle,SQLCHAR InConnectionString,SQLSMALLINT StringLength1,SQLCHAR OutConnetionString,SQLSMALLINT BufferLength,SQLSMALLINT *StringLength2Ptr,SQLSMALLINT DriverCompletion);
参数:
ConnectionHandle 连接句柄
WindowHandle 窗口句柄,应用程序可以用父窗口的句柄,或用NULL指针
InConnectionString 连接字符串长度
OutConnectionString 一个指向连接字符中的指针
BufferLength 存放连接字符串的缓冲区的长度
StringLength2Ptr 返回的连接字符串中的字符数
DriverCompletion 额外连接信息,可能取值有:SQL_DRIVER_PROMPT,SQL_DRIVER_COMPLETE,
SQL_DRIVER_COMPLETE_REQUIRED, or SQL_DRIVER_NOPROMPT.
返回值:
SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_ERROR, or SQL_INVALID_HANDLE.
成功返回SQL_SUCCESS,如果返回值为SQL_ERROR或SQL_SUCCESS_WITH_INFO,可以用函数SQLGetDiagRec获取相应SQLSTATE的值。
SQLBrowseConnect
函数SQLBrowseConnect支持以一种迭代的方式获取到数据源的连接,直到最后建立连接。它是基于客房机/服务器的体系结构,因此,本地数据库不支持该函数。
一般,我们提供部分连接信息,如果足以建立到数据源的连接,则成功建立连接,否则返回SQL__NEED__DATA,并在OutConnectionString参数中返回所需要的信息。
函数格式:
SQLRETURN SQLBrowseConnect(SQLHDBC ConnectionHandle,SQLCHAR* InConnectionString,SQLSAMLLINT StringLength1,SQLCHAR* OutConnectionString,SQLSMALLINT BufferLength,SQLSMALLINT *StringLength2Ptr);
参数:
ConnectionHandle 连接句柄
InConnectionString 指向输出字符串的指针
StringLength1 输出字符串的指针长度
OutConnectionString 指向输出字符串的指针
BufferLength 用于存放输出字符串的缓冲区的长度
StringLength2Ptr 实际返回的字符串的长度
返回值:
SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_ERROR, or SQL_INVALID_HANDLE.
成功返回SQL_SUCCESS,如果返回值为SQL_ERROR或SQL_SUCCESS_WITH_INFO,可以用函数SQLGetDiagRec获取相应SQLSTATE的值。
下面的代码讲述了如何使用ODBC API的SQLBrowseConnect函数建立同数据源的连接。
#define BRWS_LEN 100SQLHENV henv;SQLHDBC hdbc; SQLHSTMT hstmt; SQLRETURN retcode; SQLCHAR szConnStrIn[BRWS_LEN], szConnStrOut[BRWS_LEN]; SQLSMALLINT cbConnStrOut;/* Allocate the environment handle. */ retcode = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv); if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) { /* Set the version environment attribute. */ retcode = SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, SQL_OV_ODBC3, 0); if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) { /* Allocate the connection handle. */ retcode = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc); if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) { /* Call SQLBrowseConnect until it returns a value other than */ /* SQL_NEED_DATA (pass the data source name the first time). */ /* If SQL_NEED_DATA is returned, call GetUserInput (not */ /* shown) to build a dialog from the values in szConnStrOut. */ /* The user-supplied values are returned in szConnStrIn, */ /* which is passed in the next call to SQLBrowseConnect. */ lstrcpy(szConnStrIn, "DSN=Sales"); do { retcode = SQLBrowseConnect(hdbc, szConnStrIn, SQL_NTS, szConnStrOut, BRWS_LEN, &cbConnStrOut); if (retcode == SQL_NEED_DATA) GetUserInput(szConnStrOut, szConnStrIn); } while (retcode == SQL_NEED_DATA); if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO){ /* Allocate the statement handle. */ retcode = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt); if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) { /* Process data after successful connection */ ...; SQLFreeHandle(SQL_HANDLE_STMT, hstmt); } SQLDisconnect(hdbc); } } SQLFreeHandle(SQL_HANDLE_DBC, hdbc); }} SQLFreeHandle(SQL_HANDLE_ENV, henv); |
(三)可以通过三种方式构造SQL语句:在程序开发阶段确定、在运行时确定或由用户输入SQL语句
2.24 SQL操作
构造和执行SQL语句
构造SQL语句
可以通过三种方式构造SQL语句:在程序开发阶段确定、在运行时确定或由用户输入SQL语句。
在程序开发时确定的SQL语句,具有易于实现、且可在程序编码时进行测试的优势。
在程序运行时确定SQL语句提供了极大灵活性,但给程序高度带来了困难,且需更多的处理时间。由用户输入的SQL语句,极大的增强了程序的功能,但是,程序必须提供友好的人机界面,且对用户输入的语句执行一定程序的语法检查,能够报告用户错误。
执行SQL语句
应用程序的绝大部分数据库访问工作都是通过执行SQL语句完成的,在执行SQL语句之前,必须要先分配一个语句句柄,然后设置相应语句的语句属性,再执行SQL语句。当一个语句句柄使用完成后,调用函数SQLFreeHandle()释放该句柄。
SQLExecute()
SQLExecute用于执行一个准备好的语然,当语句中有参数时,用当前绑定的参数变量的值。
函数格式:
SQLRETURN SQLExecute(SQLHSTMT StatementHandle);
参数:
StatementHandle 标识执行SQL语句的句柄,可以用SQLAllocHandle()来获得。
返回值:
SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_NEED_DATA, SQL_STILL_EXECUTING, SQL_ERROR, SQL_NO_DATA, or SQL_INVALID_HANDLE
成功返回SQL_SUCCESS,如果返回值为SQL_ERROR或SQL_SUCCESS_WITH_INFO,可以用函数SQLGetDiagRec获取相应SQLSTATE的值。
SQLExecDiret()
SQLExecDirect直接执行SQL语句,对于只执行一次的操作来说,该函数是速度最快的方法。
函数格式:
SQLRETURN SQLExecDirect(SQLHSTMT StatementHandle,SQLCHAR *StatementText,SQLINTEGER TextLength);
参数:
StatementHandle 语句句柄
StatementText 要执行的SQL语然
StatementText SQL语句的长度。
返回值:
SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_NEED_DATA, SQL_STILL_EXECUTING, SQL_ERROR, SQL_NO_DATA, or SQL_INVALID_HANDLE
成功返回SQL_SUCCESS,如果返回值为SQL_ERROR或SQL_SUCCESS_WITH_INFO,可以用函数SQLGetDiagRec获取相应SQLSTATE的值。
SQLPripare()
对于需多次执行的SQL语句来说,除了使用SQLExecDirect函数之外,我们也可以在执行SQL语句之前,先准备SQL语句的执行。对于使用参数的语句,这可大提高程序执行速度。
函数格式:
SQLRETURN SQLPrepare(SQLHSTMT StatementHandle,SQLCHAR* StatementText,SQLINTEGER TextLength);
参数:
StatementHandle 语句句柄
StatementText 要执行的SQL语然
StatementText SQL语句的长度。
返回值:
SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_NEED_DATA, SQL_STILL_EXECUTING, SQL_ERROR, SQL_NO_DATA, or SQL_INVALID_HANDLE
成功返回SQL_SUCCESS,如果返回值为SQL_ERROR或SQL_SUCCESS_WITH_INFO,可以用函数SQLGetDiagRec获取相应SQLSTATE的值。
使用参数
使用参数可以使一条SQL语句多次执行,得到不同结果
SQLBindParameter
函数SQLBindParameter负责为参数定义变量,实现参数值的传递。
函数格式如下:
SQLRETURNSQLBindParameter(SQLHSTMT StatementHandle,SQLUSMALLINT ParameterNumber,SQLSMALLINT InputOutputType,SQLSMALLINT ValueType,SQLSMALLINT ParameterType,SQLUINTEGER ColumnSize,SQLSMALLINT DecimalDigits,SQLPOINTER ParameterValuePtr,SQLINTEGER BufferLength,SQLINTEGER *StrLen_or_IndPtr);
参数:
StatementHandle 语句句柄
ParameterNumber 绑定的参数在SQL语句中的序号,在SQL中,所有参数从左到右顺序编号,从1开始。SQL语句执行之前,应该为每个参数调用函数SQLBindParameter绑定到某个程序变量。
InputOutputType 参数类型,可为SQL_PARA_INPUT, SQL_PARAM_INPUT_OUTPUT, SQL_PARAM_OUTPUT。
ParameterType 参数数据类型
ColumnSIze 参数大小
DecimalDigits 参数精度
ParameterValutePtr 指向程序中存放参数值的缓冲区的指针
BufferLength 程序中存放参数值的缓冲区的字节数
StrLen_or_IndPtr 指向存放参数ParameterValuePtr的缓冲区指针
返回值:
SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_ERROR, or SQL_INVALID_HANDLE
成功返回SQL_SUCCESS,如果返回值为SQL_ERROR或SQL_SUCCESS_WITH_INFO,可以用函数SQLGetDiagRec获取相应SQLSTATE的值。
执行时传递参数
对于某些文本文档或位图文件,要占用大量的存储空间。因此,在数据源传递这些数据时,可以分开传递。有两个函数可完成这个工作。
函数格式:
SQLRETURN SQLPutData(SQLHSTMT StatementHandle,SQLPOINTER DataPtr,SQLINTEGER StrLen_or_Ind);
参数:
StatementHandle 参数句柄
DataPtr 指向包含实际数据的缓冲区指针。
StrLen_or_Lnd 缓冲区长度
返回值:
SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_ERROR, or SQL_INVALID_HANDLE
成功返回SQL_SUCCESS,如果返回值为SQL_ERROR或SQL_SUCCESS_WITH_INFO,可以用函数SQLGetDiagRec获取相应SQLSTATE的值。
函数格式:
SQLRETURNSQLParamData(SQLHSTMT StatementHandle,SQLPOINTER *ValuePtrPtr);
参数:
StatementHandle 参数句柄
ValuePtrPtr 指向缓冲区地址的指针
返回值:
SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_ERROR, or SQL_INVALID_HANDLE
成功返回SQL_SUCCESS,如果返回值为SQL_ERROR或SQL_SUCCESS_WITH_INFO,可以用函数SQLGetDiagRec获取相应SQLSTATE的值。
下面的代码展示如何使用这两个函数
#define MAX_DATA_LEN 1024 SQLINTEGER cbPartID = 0, cbPhotoParam, cbData; SQLUINTEGER sPartID; szPhotoFile; SQLPOINTER pToken, InitValue; SQLCHAR Data[MAX_DATA_LEN]; SQLRETURN retcode; SQLHSTMT hstmt; retcode = SQLPrepare(hstmt, "INSERT INTO PICTURES (PARTID, PICTURE) VALUES (?, ?)", SQL_NTS); if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) { /* Bind the parameters. For parameter 2, pass */ /* the parameter number in ParameterValuePtr instead of a buffer */ /* address. */ SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_ULONG, SQL_INTEGER, 0, 0, &sPartID, 0, &cbPartID); SQLBindParameter(hstmt, 2, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, 0, 0, (SQLPOINTER) 2, 0, &cbPhotoParam); /* Set values so data for parameter 2 will be */ /* passed at execution. Note that the length parameter in */ /* the macro SQL_LEN_DATA_AT_EXEC is 0. This assumes that */ /* the driver returns "N" for the SQL_NEED_LONG_DATA_LEN */ /* information type in SQLGetInfo. */ cbPhotoParam = SQL_LEN_DATA_AT_EXEC(0); sPartID = GetNextID(); /* Get next available employee ID */ /* number. */ retcode = SQLExecute(hstmt); /* For data-at-execution parameters, call SQLParamData to */ /* get the parameter number set by SQLBindParameter. */ /* Call InitUserData. Call GetUserData and SQLPutData */ /* repeatedly to get and put all data for the parameter. */ /* Call SQLParamData to finish processing this parameter */ while (retcode == SQL_NEED_DATA) { retcode = SQLParamData(hstmt, &pToken); if (retcode == SQL_NEED_DATA) { InitUserData((SQLSMALLINT)pToken, InitValue); while (GetUserData(InitValue, (SQLSMALLINT)pToken, Data, &cbData)) SQLPutData(hstmt, Data, cbData); } }} VOID InitUserData(sParam, InitValue)SQLPOINTER InitValue;{ SQLCHAR szPhotoFile[MAX_FILE_NAME_LEN]; /* Prompt user for bitmap file containing employee */ /* photo. OpenPhotoFile opens the file and returns the */ /* file handle. */ PromptPhotoFileName(szPhotoFile); OpenPhotoFile(szPhotoFile, (FILE *)InitValue); break; } BOOL GetUserData(InitValue, sParam, Data, cbData)SQLPOINTER InitValue; SQLCHAR *Data;SQLINTEGER *cbData;BOOL Done;{ /* GetNextPhotoData returns the next piece of photo */ /* data and the number of bytes of data returned */ /* (up to MAX_DATA_LEN). */ Done = GetNextPhotoData((FILE *)InitValue, Data, MAX_DATA_LEN, &cbData); if (Done) { ClosePhotoFile((FILE *)InitValue); return (TRUE); } return (FALSE); } 记录的添加、删除和更新 应用程序对数据源的数据更新可以通过三种方式实现:使用相应的SQL语句在数据源上执行;调用函数SQLSetPos实现记录集的定义更新;调用函数SQLBulkOperations实现数据的更新。 直接在数据源上执行SQL语句的方式,可以适用于任何的ODBC数据源,但是,对于后两种更新方式,有的数据源并不支持,应用程序可以调用函数SQLGetInfo确定数据源是否支持这两种方式。 定位更新和删除 要使用定位更新和删除功能,要按如下顺序: 1)取回记录集: 2)定位到要进行更新或删除操作的行 3)执行更新或删除操作 参考如下的代码:
用SQLBulkOperations()更新数据 函数SQLBulkOperations的操作是基于当前行集的,在调用函数SQLBulkOperations之前,必须先调用函数SQLFetch或SQLFetchScroll获取行集,然后,再执行修改或删除操作。 函数格式: SQLRETURN SQLBulkOperations(SQLHSTMT StatementHandle,SQLUSMALLINT Operation); 参数: StatementHandle 参数句柄 Operation 标识执行的操作类型,可以是以下几种之一: SQL_ADD SQL_UPDATE_BY_BOOKMARK SQL_DELETE_BY_BOOKMARK SQL_FETCH_BY_BOOKMARK 返回值: SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_NEED_DATA, SQL_STILL_EXECUTING, SQL_ERROR, or SQL_INVALID_HANDLE 成功返回SQL_SUCCESS,如果返回值为SQL_ERROR或SQL_SUCCESS_WITH_INFO,可以用函数SQLGetDiagRec获取相应SQLSTATE的值。 取回查询结果 绑定列 从数据源取回的数据存放在应用程序定义的变量中,因此,我们必须首先分配与记录集中字段相对应的变量,然后通过函数SQLBindCol将记录字段同程序变量绑定在一起。对于长记录字段,可以通过调用函数SQLGetData()直接取回数据。 绑定字段可以根据自己的需要,全部绑定,也可以绑定其中的某几个字段。 记录集中的字段可以在任何时候绑定,但是,新的绑定只有当下一次从数据源中取数据时执行的操作才有郊,而不会对已经取回的数据产生影响。 函数格式: SQLRETURN SQLBindCol(SQLHSTMT StatementHandle,SQLUSMALLINTColumnNumber, SQLSMALLINT TargetType, SQLPOINTERTargetValuePtr,SQLINTEGERBufferLength,SQLINTEGER *StrLen_or_IndPtr); 参数: StatementHandle 语句句柄 ColumnNumber 标识要绑定的列号。数据列号从0开始升序排列,其中第0列用作书签。如果没有使用书签,则列号从1开始。 TargetType 数据类型 TargetValuePtr 绑定到数据字段的缓冲区的地址 BufferLength 缓冲区长度 StrLen_or_IndPtr 指向绑定数据列使用的长度的指针. 返回值: SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_ERROR, or SQL_INVALID_HANDLE 成功返回SQL_SUCCESS,如果返回值为SQL_ERROR或SQL_SUCCESS_WITH_INFO,可以用函数SQLGetDiagRec获取相应SQLSTATE的值。 SQLFetch() 函数SQLFetch用于将记录集中的下一行变为当前行,并把所有捆绑过的数据字段的数据拷贝到相应的缓冲区。 函数格式: SQLRETURN SQLFetch(SQLHSTMT StatementHandle) 参数: StatementHandle 语句句柄 返回值: SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_NO_DATA, SQL_STILL_EXECUTING, SQL_ERROR, or SQL_INVALID_HANDLE. 成功返回SQL_SUCCESS,如果返回值为SQL_ERROR或SQL_SUCCESS_WITH_INFO,可以用函数SQLGetDiagRec获取相应SQLSTATE的值。 光标 应用程序获取数据是通过光标(Cursor)来实现的,在ODBC中,主要有三种类型的光标: 单向光标: 单向光标只能向前移动,要返回记录集的开始位置,程序必须先关闭光标,再打开光标,它对于只需要浏览一次的应用非常有用,而且效率很高。对于需要光标前后移动的,单向光村不适用。 可滚动光标 可滚动光标通常基于图形用户界面的程序,用户通过屏幕向前或向后滚动,浏览记录集中的数据。 块光标 所谓块光标,可以理解为执行多行的光标,它所指向的行称为行集。对于网络环境下的应用,使用块光标可以在一定程序上减轻网络负载。 要使用块光标,应完成以下工作: 1)设定行集大小 2)绑定行集缓冲区 3)设定语句句柄属性 4)取行行集结果 由于块光标返回多行记录,应用程序必须把这多行的数据绑定到某些数据组中,这些数据称为行集缓冲区。绑定方式有两种:列方式和行方式。 ODBC光标库 有些应用程序不支持可滚动光标和块光标,ODBC SDK 提供了一个光标库(ODBCCR32.DLL),在应用程序中可通过设置连接属性(SQL_STTR_ODBC_CURSORS)激活光标库。 参考如下代码:
2.25 断开同数据源的连接 当完成对数据库操作后,就可以调用SQLDisconnect函数关闭同数据源连接。 当该句柄的事务操作还未完成时,应用程序调用SQLDisconnect,这时,驱动器返回SQLSTATE 25000,表明此次事务没有变化且连接还打开。 在应用程序调用SQLDisconnect之前没有释放与之相连的描述时,当成功的与数据源断开连接后,驱动器自动释放与之相连的参数、描述器等。但当与之相连的某一个参数还在执行异步操作时,SQLDisConnect返回SQL_ERROR,且SQLSTATE的值置为HY010. 2.26 释放ODBC环境 最后一步就是释放ODBC环境参数了。 2.3 ODBC API编程总结 关系数据库的研究与应用是当今计算机界最活跃的领域之一,各种数据库产品行行色色,各有千秋;这种情况一方面给用户带来了好处,另一方面又给应用程序的移植带来了困难。尤其是在客户机/服务器体系结构中,当用户要从客户机端访问不同的服务器,而这些服务器的数据库系统又各不相同,数据库之间的互连访问就成为一个难题,因此,微软公司提出了ODBC。由于ODBC思想上的先进性及其微软公司的开放策略,ODBC现在已经成为事实上的工业标准,它是目前数据库应用方面很多问题强有力的解决方案,正逐步成为Windows平台上的标准接口。 ODBC是一种用来在相关或不相关的数据库管理系统(DBMS)中存取数据的标准应用程序设计接口(API)。它的基本思想是为用户提供简单、标准、透明、统一的数据库联接的公共编程接口,在各个厂家的支持下能为用户提供一致的应用开发界面,使应用程序独立于数据库产品,实现各种数据库之间的通信。开发厂商根据ODBC的标准去实现底层的驱动程序,它对用户是透明的。 作为一种数据库联接的标准技术,ODBC有以下几个主要特点: 1、ODBC是一种使用SQL的程序设计接口; 2、ODBC的设计是建立在客户机/服务器体系结构基础之上的; 3、ODBC使应用程序开发者避免了与数据源联接的复杂性; 4、ODBC的结构允许多个应用程序访问多个数据源,即应用程序与数据源的关系是多对多的关系。 (四) 3、 使用MFC访问ODBC数据源 3.1 概述 Visual C++的MFC类库定义了几个数据库类。在利用ODBC编程时,经常要使用到CDatabase(数据库类),CRecordSet(记录集类)和CRecordView(可视记录集类)。 其中: CDatabase类对象提供了对数据源的连接,通过它你可以对数据源进行操作。 CRecordView类对象能以控制的形式 显示数据库记录。这个视图是直接连到一个CRecordSet对象的表视图。 CRecordSet类对象提供了从数据源 中提取出的记录集。CRecordSet对象通常用于两种形式: 动态行集(dynasets)和快照集(snapshots)。动态行集能保 持与其他用户所做的更改保持同步。快照集则是数据的一个静态视图。每一种形式在记录集被打开时都提供一组记录,所不同的是,当你在一个动态行集里滚 动到一条记录时,由其他用户或是你应用程序中的其他记录集对该记录所做的更改会相应地显示出来。 Visual C++提供了几种记录集,可以用来定制应用程序的工作方式。查看这些不同选项的最快方式要兼顾速度和特征。你会发现,在很多情况下,如果想添加特征,就必须付出程序执行速度降低的代价。下面告诉你一些可以自由支配的记录集选项。更重要的是,要告诉你从这个选项可以获得更快的速度还是更多的特征。 1、Snapshot(快照) 这个选项要Visual C++在一次快照中下载整个查询。换言之,及时快速地给数据库内容拍照,并把它作为未来工作的基础。这种方法有三个缺点。第一,你看不到别人在网络上做的更新,这可能意味着你的决定是建立在老信息的基础上。第二,一次就下载所有这些记录,这意味着在下载期间给网络增加了沉重的负担。第三,记录下载时用户会结束等待,这意味着网络的呼叫性能变得更低。然而这种方法也有两个优点。第一,记录一旦被下载,该工作站所需的网络活动几乎就没有了棗这为其它请求释放了带宽。总之,你会看到网络的吞吐量增大了。第二,因为所有被申请的记录都在用户的机器上,所以用户实际上会得到应用程序更佳的总体性能。你可能想把快照的方法限制在较小的数据库上使用,原因在于快照适用于用户请求信息而不适用于数据编辑会话。 2、Dynaset(动态集) 使用这个选项时,Visual C++创建指向所请求的每个记录的实际指针。另外,只有填充屏幕时实际需要的记录是从服务器上下载来的。这种方法的好处很明显。几乎马上就能在屏幕上看到记录。而且还会看到其它用户对数据库所做的更改。最后,其它用户也会看到你做的更改,因为动态集在你更改记录时被上载到服务器上。很明显,这种方法要求对服务器的实时访问,它减小了网络总吞吐量并降低了应用程序的性能。这个选项适合于创建用户要花费很多时间来编辑数据的应用程序。同时,它也是大型数据库的最佳选择,原因在于只需下载用户实际需要的信息。 3.2 应用ODBC编程 可以应用AppWizard来建立一个ODBC的应用程序框架,也可以直接使用ODBC来进行数据库编程,这时,应包括头文件afxdb.h。 应用ODBC编程两个最重要的类是CDatabase和CRecordSet,但在应用程序中,不应直接使用CRecordSet类,而必须从CRecordSet类产生一个导出类,并添加相应于数据库表中字段的成员变量。随后,重载CRecordset类的成员函数DoFieldExchange,该函数通过使用RFX函数完成数据库字段与记录集域数据成员变量的数据交换,RFX函数同对话框数据交换(DDX)机制相类似,负责完成数据库与成员变量间的数据交换。 下面举例说明在Visual C++环境中ODBC 的编程技巧: 3.21 数据库连接 在CRecordSet类中定义了一个成员变 量m_pDatabase: CDatabase *m_pDatabase; 它是指向对象数据库类的指针。如果在CRecordSet类对象调用Open()函数之前,将一个已经打开的CDatabase类对象指针传给m_pDatabase,就能共享相同 的CDatabase类对象。如:
3.22 查询记录 查询记录使用CRecordSet::Open()和 CRecordSet::Requery()成员函数。在使用CRecordSet类对象之前,必须使用 CRecordSet::Open()函数来获得有效的记录集。一旦已经使用过CRecordSet::Open() 函数,再次查询时就可以应用CRecordSet::Requery()函数。在调 用CRecordSet::Open()函数时,如果已经将一个已经打开的CDatabase 对象指针传给CRecordSet类对象的m_pDatabase成员变量,则使 用该数据库对象建立ODBC连接;否则如果m_pDatabase为空指 针,就新建一个CDatabase类对象并使其与缺省的数据源 相连,然后进行CRecordSet类对象的初始化。缺省数据源 由GetDefaultConnect()函数获得。你也可以提供你所需要的SQL 语句,并以它来调用CRecordSet::Open()函数,例如:
如果没有指定参数,程序则使 用缺省的SQL语句,即对在GetDefaultSQL()函数中指定的SQL语 句进行操作:
对于GetDefaultSQL()函数返回的表名, 对应的缺省操作是SELECT语句,即:
查询过程中也可以利用CRecordSet的 成员变量m_strFilter和m_strSort来执行条件查询和结果排序。m_strFilter 为过滤字符串,存放着SQL语句中WHERE后的条件串;m_strSort 为排序字符串,存放着SQL语句中ORDERBY后的字符串。 如:
对应的SQL语句为:
除了直接赋值给m_strFilter以外,还 可以使用参数化。利用参数化可以更直观,更方便地 完成条件查询任务。使用参数化的步骤如下: (1).声明参变量:
(2).在构造函数中初始化参变量
(3).将参变量与对应列绑定
完成以上步骤之后就可以利用参变量进行条件查询了:
参变量的值按绑定的顺序替换 查询字串中的“?”适配符。 如果查询的结果是多条记录的 话,可以用CRecordSet类的函数Move(),MoveNext(),MovePrev(),MoveFirst() 和MoveLast()来移动光标 3.23 增加记录 增加记录使用AddNew()函数,要求数据库必须是以允许增加的方式打开:
3.24 删除记录 直接使用Delete()函数,并且在调用Delete() 函数之后不需调用Update()函数:
3.25 修改记录 修改记录使用Edit()函数:
3.26 统计记录 统计记录用来统计记录集的总数。可以先声明一个CRecordset对象m_pSet。再绑定一个变量m_lCount,用来统计记录总数。执行如下语句:
RecordCount即为要统计的记录数。 或如下:
count为记录总数。 (五)虽然通过CRecordSet类,我们可以完成大多数的查询操作,而且在CRecordSet::Open()函数中也可以提供SQL语句 3.27 执行SQL语句 虽然通过CRecordSet类,我们可以完成 大多数的查询操作,而且在CRecordSet::Open()函数中也可以 提供SQL语句,但是有的时候我们还想进行一些其他操作,例如建立新表,删除表,建立新的字段等等,这时就需要使用到CDatabase类的直接执行SQL语句的机制。通过调用CDatabase::ExecuteSQL()函数来完成SQL语句的直接执行: 如下代码所示
应当指出的是,由于不同DBMS提 供的数据操作语句不尽相同,直接执行SQL语句可能会破坏软件的DBMS无关性,因此在应用中应当慎用此类操作。 3.28 注意 从CRecordSet导出的类中如果包含DataTime类型的数据,在VC中是用CTime类型来替代的,这时,构造函数没有赋于缺省值。这时,我们应当手工赋值。如下所示:
4.2 应用DAO编程
其中参数一包括要打开的文件的全路径名。 4.22 查询记录 一个DAO recordset对象,代表一个数据记录的集合,该集合是一个库表或者是一个查询的运行结果中的全部记录。CDaoRecorset对象有三种类型:表、动态集、快照。 通常情况下,我们在应用程序中可以使用CDaoRecordset的导出类,这一般是通过ClassWizard或AppWizard来生成的。但我们也可以直接使用CDaoRecordset类生成的对象。此时,我们可以动态地绑定recordset对象的数据成员。 如下代码所示:
4.23 添加记录 添加记录用AddNew函数,此时用SetFieldValue来进行赋值。 如下代码所示:
此时,要注意,日期时间型数据要用SetDataTime函数来赋值,这里面要用到COleVariant类型数据,具体用法可以参考有关帮助。 4.24 修改记录 修改记录用Edit()函数,把记录定位到要修改的位置,调用Edit函数,修改完成后,调用Update函数。 如下代码所示:
4.25 删除记录 删除记录用Delete()函数,使用后不需调用Update()函数。 4.26 统计记录 可以使用如下代码来统计记录数:
如果是统计一张表中总记录,可以使用CDaoTableDef对象,如下代码所示:
不能用CDaoRecordset对象的GetRecordCount()来取得记录数。 4.3 总结 使用DAO技术可以便我们方便的访问Microsoft Jet引擎数据库,由于Microsoft Jet不支持多线程,因此,必须限制调用到应用程序主线程的所有DAO。 (六)OLE DB的存在为用户提供了一种统一的方法来访问所有不同种类的数据源。OLE DB可以在不同的数据源中进行转换 5 使用OLE DB 5.1 概述 OLE DB的存在为用户提供了一种统一的方法来访问所有不同种类的数据源。OLE DB可以在不同的数据源中进行转换。利用OLE DB,客户端的开发人员在进行数据访问时只需把精力集中在很少的一些细节上,而不必弄懂大量不同数据库的访问协议。 OLE DB是一套通过COM接口访问数据的ActiveX接口。这个OLE DB接口相当通用,足以提供一种访问数据的统一手段,而不管存储数据所使用的方法如何。同时,OLE DB还允许开发人员继续利用基础数据库技术的优点,而不必为了利用这些优点而把数据移出来。 5.2 使用ATL使用OLE DB数据使用程序 由于直接使用OLE DB的对象和接口设计数据库应用程序需要书写大量的代码。为了简化程序设计,Visual C++提供了ATL模板用于设计OLE DB数据应用程序和数据提供程序。 利用ATL模板可以很容易地将OLE DB与MFC结合起来,使数据库的参数查询等复杂的编程得到简化。MFC提供的数据库类使OLE DB的编程更具有面向对象的特性。Viual C++所提供用于OLE DB的ATL模板可分为数据提供程序的模板和数据使用程序的模板。 使用ATL模板创建数据应用程序一般有以下几步骤: 创建应用框架 加入ATL产生的模板类 在应用中使用产生的数据访问对象 不用ATL使用OLE DB数据使用程序 利用ATL模板产生数据使用程序较为简单,但适用性不广,不能动态适应数据库的变化。下面我们介绍直接使用MFC OLE DB类来生成数据使用程序。 模板的使用 OLE DB数据使用者模板是由一些模板组成的,包括如下一些模板,下面对一些常用类作一些介绍。 会话类 CDataSource类 CDataSource类与OLE DB的数据源对象相对应。这个类代表了OLE DB数据提供程序和数据源之间的连接。只有当数据源的连接被建立之后,才能产生会话对象,可以调用Open来打开数据源的连接。 CSession类 CSession所创建的对象代表了一个单独的数据库访问的会话。一个用CDataSource类产生的数据源对象可以创建一个或者多个会话,要在数据源对象上产生一个会话对象,需要调用函数Open()来打开。同时,会话对象还可用于创建事务操作。 CEnumeratorAccessor类 CEnumeratorAccessor类是用来访问枚举器查询后所产生的行集中可用数据提供程序的信息的访问器,可提供当前可用的数据提供程序和可见的访问器。 访问器类 CAcessor类 CAccessor类代表与访问器的类型。当用户知道数据库的类型和结构时,可以使用此类。它支持对一个行集采用多个访问器,并且,存放数据的缓冲区是由用户分配的。 CDynamicAccessor类 CDynamicAccessor类用来在程序运行时动态的创建访问器。当系统运行时,可以动态地从行集中获得列的信息,可根据此信息动态地创建访问器。 CManualAccessor类 CManualAccessor类中以在程序运行时将列与变量绑定或者是将参数与变量捆定。 行集类 CRowSet类 CRowSet类封装了行集对象和相应的接口,并且提供了一些方法用于查询、设置数据等。可以用Move()等函数进行记录移动,用GetData()函数读取数据,用Insert()、Delete()、SetData()来更新数据。 CBulkRowset类 CBulkRowset类用于在一次调用中取回多个行句柄或者对多个行进行操作。 CArrayRowset类 CArrayRowset类提供用数组下标进行数据访问。 命令类 CTable类 CTable类用于对数据库的简单访问,用数据源的名称得到行集,从而得到数据。 CCommand类 CCommand类用于支持命令的数据源。可以用Open()函数来执行SQL命令,也可以Prepare()函数先对命令进行准备,对于支持命令的数据源,可以提高程序的灵活性和健壮性。 在stdafx.h头文件里,加入如下代码。 #include extern CComModule _Module; #include #include #include // if you are using schema templates 在stdafx.cpp文件里,加入如下代码。 #include CComModule _Module; 决定使用何种类型的存取程序和行集。 获取数据 在打开数据源,会话,行集对象后就可以获取数据了。所获取的数据类型取决于所用的存取程序,可能需要绑定列。按以下步骤。 用正确的命令打开行集对象。 如果使用CManualAccessor,在使用之前与相应列进行绑定。要绑定列,可以用函数GetColumnInfo,如下所示:
用while循环来取数据。在循环中,调用MoveNext来测试光标的返回值是否为S_OK,如下所示:
在while循环内,可以通过不同的存取程序获取数据。 如果使用的是CAccessor类,可以通过使用它们的数据成员进行直接访问。如下所示: 如果使用的是CDynamicAccessor 或CDynamicParameterAccessor 类,可以通过GetValue或GetColumn函数来获取数据。可以用GetType来获取所用数据类型。如下所示:
如果使用的是CManualAccessor,可以指定自己的数据成员,绑定它们。就可以直接存取。如下所示:
决定行集的数据类型 在运行时决定数据类型,要用动态或手工的存取程序。如果用的是手工存取程序,可以用GetColumnInfo函数得到行集的列信息。从这里可以得到数据类型。 5.4 总结 由于现在有多种数据源,,想要对这些数据进行访问管理的唯一途径就是通过一些同类机制来实现,如OLE DB。高级OLE DB结构分成两部分:客户和提供者。客户使用由提供者生成的数据。 就像其它基于COM的多数结构一样,OLE DB的开发人员需要实现很多的接口,其中大部分是模板文件。 当生成一个客户对象时,可以通过ATL对象向导指向一个数据源而创建一个简单的客户。ATL对象向导将会检查数据源并创建数据库的客户端代理。从那里,可以通过OLE DB客户模板使用标准的浏览函数。 当生成一个提供者时,向导提供了一个很好的开端,它们仅仅是生成了一个简单的提供者来列举某一目录下的文件。然后,提供者模板包含了OLE DB支持的完全补充内容。在这种支持下,用户可以创建OLE DB提供者,来实现行集定位策略、数据的读写以及建立书签。
|