VC数据库编程_3 ---ODBC API编程

数据库及其编程API来源于不同的背景,开发人员可以从众多的数据库中选择一种,每种数据库都有自己的一套编程API,这就为数据库编程造成了很大的局限性。SQL是标准化数据库编程接口的一种尝试,然而各种数据库所支持的SQL又有所不同。
ODBC的设计目的是允许访问多种数据库,ODBC为数据库供应商提供了一致的ODBC驱动程序标准,遵循这个标准开发的数据库驱动程序,都可以被开发人员通过ODBC API透明地访问,而不必关心实际的数据库是什么。在这里,ODBC所做的,就是接收开发人员的数据库操作指令,调用相应的ODBC驱动程序,向一个数据库或者向多个数据库发送数据,并可接收来自数据库的数据。
ODBC提供了访问数据库的标准,这使得开发人员将精力集中在应用程序以及用户接口的开发上,而不必考虑与之相连的数据库。为了进一步简化开发工作,ODBC把API层实现为SQL的映射器。通常数据库开发人员将标准的SQL语句发送给ODBC驱动程序,再由ODBC驱动程序将这个SQL语句映射成数据库可以支持的SQL语句。
本章首先对ODBC API的概貌进行简要介绍,然后讲述利用ODBC API进行数据库开发的技巧,最后将通过具体数据库开发实例,详细讲述通过ODBC API开发数据库应用程序的方法和过程。
5.1 了解ODBC API
ODBC是一种使用SQL的程序设计接口。使用ODBC让应用程序的编写者避免了与数据源相联的复杂性。这项技术目前已经得到了大多数DBMS厂商们的广泛支持。ODBC是一种使用SQL 的程序设计接口。使用ODBC让应用程序的编写者避免了与数据源相联的复杂性。这项技术目前已经得到了大多数DBMS厂商们的广泛支持。
Microsoft Developer Studio为大多数标准的数据库格式提供了32位ODBC驱动器。这些标准数据格式包括有:SQL Server,Access,Paradox,dBase,FoxPro,Excel,Oracle 以及Microsoft Text。如果用户希望使用其他数据格式,用户需要相应的ODBC驱动器及DBMS。
ODBC API是一个内容丰富的数据库编程接口,包括60多个函数、SQL数据类型以及常量的声明。ODBC API 是独立于DBMS和操作系统的,而且它与编程语言无关。ODBC API 以X/Open和ISO/IEC中的CLI规范为基础,ODBC 3.0完全实现了这两种规范,并添加了基于视图的数据库应用程序开发人员所需要的共同特性,例如可滚动光标。ODBC API中的函数由特定DBMS驱动程序的开发人员实现,应用程序用这些驱动程序调用函数,以独立于DBMS的方式访问数据。
ODBC API涉及了数据源连接与管理、结果集检索、数据库管理、数据绑定、事务操作等内容,目前的最高版本是3.0。
5.2 ODBC API编程步骤
通常使用ODBC API开发数据库应用程序需要经过如下步骤:
" 连接数据源。
" 分配语句句柄。
" 准备并执行SQL语句。
" 获取结果集。
" 提交事务。
" 断开数据源连接并释放环境句柄。
下面对上述步骤做详细的介绍。
5.2.1 步骤1:连接数据源
为了连接数据源,必须要建立一个数据源连接的环境句柄。通过调用SQLAllocEnv函数实现对环境句柄的分配,在ODBC 3.0里,这个函数已经被SQLAllocHandle取代,但是熟悉ODBC API的开发人员还是习惯用这个函数建立环境句柄,因为VC++开发平台有一个映射服务,这个服务将程序代码对函数SQLAllocEnv的调用转向对函数SQLAllocHandle的调用。
这里有必要对"环境句柄"这个概念进行说明。句柄是指向一个特殊结构的指针,而环境指的是驱动程序管理器需要为该驱动程序存储的有关系统和数据源的一般信息。由于这个时候还没有建立同数据源的连接,驱动程序还并不知道该使用哪一个驱动程序来完成这个任务,所以这个任务只能由驱动程序管理器来完成,利用这个环境句柄保留信息直到被使用。
使用函数SQLAllocEnv创建环境句柄的语法如下:
HENV henv;
RETCODE rcode;
rcode = ::SQLAllocEnv(SQL_HANDLE_ENV, SQL_NULL, & henv);
if(rcode == SQL_SUCCESS)   // 环境句柄创建成功
{
// 执行其它操作
…………
}
完成了环境句柄的创建以后,还要建立一个连接句柄。连接句柄的创建函数是SQLAllocConnect,其调用语法如下:
HDBC   hdbc;
retcode = ::SQLAllocConnect( m_henv, & hdbc);
if(rcode == SQL_SUCCESS)   // 连接句柄创建成功
{
// 执行其它操作
…………
}
完成了环境句柄和连接句柄的创建以后,就可以进行实际的数据源连接了。完成数据源连接的函数是SQLConnect,其调用语法如下:
m_retcode = :: SQLConnect( m_hdbc,
                       (PUCHAR)pszSourceName, SQL_NTS,
                       (PUCHAR)pszUserId, wLengthUID,
                       (PUCHAR)pszPassword, wLengthPSW );
if(rcode == SQL_SUCCESS)   // 数据源连接成功
{
// 执行其它操作
…………
}
到此,应用程序同数据源的连接已经完成。
有些时候,ODBC数据源并不是事先在用户的计算机里安装好了的,这时就需要应用程序能够动态创建ODBC数据源。ODBC API提供了动态创建数据源的函数SQLConfigDataSource。该函数的语法如下:
BOOL SQLConfigDataSource(HWND hwndParent,
         WORD fRequest,
         LPCSTR lpszDriver,
         LPCSTR lpszAttributes);
参数hwndParent用于指定父窗口句柄,在不需要显示创建数据源对话框时,可以将该参数指定为NULL;参数fRequest用于指定函数的操作内容,函数SQLConfigDataSource能够实现的操作内容由参数fRequest制定,参数fRequest取值如下:
ODBC_ADD_DSN:创建数据源;
ODBC_CONFIG_DSN:配置或者修改已经存在的数据源;
ODBC_REMOVE_DSN:删除已经存在的数据源;
ODBC_ADD_SYS_DSN:创建系统数据源;
ODBC_CONFIG_SYS_DSN:配置或者修改已经存在的系统数据源;
ODBC_REMOVE_SYS_DSN:删除已经存在的系统数据源;
ODBC_REMOVE_DEFAULT_DSN:删除缺省的数据源。
参数lpszDriver用于指定ODBC数据源的驱动程序类别,例如,为了指定Access数据源,该参数应赋以字符串"Microsoft Access Driver (*.mdb)\0"。 参数lpszAttributes用于指定ODBC数据源属性,例如:
"DSN=MYDB\0DBQ=D:\\Database\\Friends.mdb\0DEFAULTDIR=D:\\DATABASE\0\0"
该字符串指定数据源名称(DSN)为MYDB,数据库文件(DBQ)为D:\Database\Friends.mdb,缺省数据库文件路径(DEFAULTDIR)为D:\DATABASE。
通过调用如下代码可以通过应用程序动态创建数据源MYDB:
BOOL CreateDSN()
{
char* szDesc;
int mlen;
szDesc=new char[256];
sprintf(szDesc,"DSN=%s: DESCRIPTION=TOC support source: \
       DBQ=%s: FIL=MicrosoftAccess: \
       DEFAULTDIR=D:\\Database:: ","TestDB","D:\\Friends.mdb");
mlen = strlen(szDesc);
for (int i=0; i<mlen; i++){
    if (szDesc[i] == ':')   szDesc[i] = '\0';
   }

   if (FALSE == SQLConfigDataSource(NULL,ODBC_ADD_DSN,
          "Microsoft Access Driver (*.mdb)\0",
          (LPCSTR)szDesc))
    return FALSE; // 创建数据源失败。
   else
    return TRUE; // 创建数据源成功。
}
5.2.2 步骤2:分配语句句柄
通常将ODBC中的语句看作SQL语句。前面已经提到,ODBC同数据库的SQL接口通信,驱动程序将ODBC的SQL映射到驱动程序的SQL。但是ODBC的SQL还携带了一些属性信息,用于定义数据源连接的上下文,有些语句要求特殊的参数以便能够执行,因此,每个语句都有一个指向定义语句所有属性结构的句柄。
语句句柄的分配同环境句柄的分配相似,通过函数SQLAllocStmt实现,该函数的调用语法如下:
HSTMT    hstmt;
RETCODE rcode;
m_retcode = :: SQLAllocStmt(hdbc, &hstmt );
if(rcode == SQL_SUCCESS)   // 连接句柄创建成功
{
// 执行其它操作
…………
}
5.2.3 步骤3:准备并执行SQL语句
对于不同的应用程序需求,要准备的SQL语句也一定不一样。通常的SQL语句包括SELECT、INSERT、UPDATA、DELETE、DROP等。
准备和执行一个SQL语句的方法有两种,第一种是使用SQLExecDirect函数,可以一次执行一个SQL语句。很多请求都可以使用这个方法。调用SQLExecDirect函数的语法如下:
LPCSTR pszSQL;
strcpy(pszSQL, "SELECT * FROM EMPLOYEES");
retcode = ::SQLExecDirect(hstmt, (UCHAR*)pszSQL, SQL_NTS );
if(rcode == SQL_SUCCESS)   // SQL语句执行成功
{
// 执行其它操作
…………
}
但是有些请求需要多次执行同一条语句,为此,ODBC提供了SQLPrepare函数和SQLExecute函数。调用的时候,只需要调用一次SQLPrepare函数,然后调用若干次SQLExecute函数。实际上,函数SQLExecDirect将SQLPrepare和SQLExecute的功能集中到了一起,多次调用SQLExecDirect显然比调用一次SQLPrepare再调用若干次SQLExecute效率高。调用SQLPrepare和SQLExecute函数的语法如下:
LPCSTR pszSQL;
strcpy(pszSQL, "SELECT * FROM EMPLOYEES");
m_retcode = ::SQLPrepare( hstmt, (UCHAR*)pszSQL, SQL_NTS );
if(rcode == SQL_SUCCESS)   // SQL语句准备成功
{
// 执行其它操作
…………
}
retcode = :: SQLExecute (hstmt, (UCHAR*)pszSQL, SQL_NTS );
if(rcode == SQL_SUCCESS)   // SQL语句执行成功
{
// 执行其它操作
…………
}
5.2.4 步骤4:获取结果集
SQL语句执行成功以后,应用程序必须准备接收数据,应用程序需要把SQL语句执行结果绑定到一个本地缓存变量里。但是SQL执行语句执行的结果并不是直接传送给应用程序,而是在应用程序准备接收数据的时候通知驱动程序其已经准备好接收数据,应用程序通过调用SQLFetch函数返回结果集的一行数据。
由于返回的数据是存放在列中的,因此应用程序必须调用SQLBindCol函数绑定这些列。通常接收结果集时需要依次进行以下操作:
" 返回列的个数,执行SQLNumResultCols函数。
" 给出列中数据的有关信息,例如列的名称、数据类型和精度等,执行SQLDescribeCol函数。
" 把列绑定到应用程序的变量里,执行SQLBindCol函数。
" 获取数据,执行SQLFetch函数。
" 获取长数据,执行SQLGetData函数。
应用程序首先调用SQLNumResultCols函数,获知每个记录里有多少列,调用SQLDescribeCol函数取得每列的属性,然后调用SQLBindCol函数将列数据绑定到指定的变量里,最后调用SQLFetch函数或者SQLGetData函数获取数据。
对于其它的SQL语句,应用程序重复这个过程。这个过程代码如下:
retcode = ::SQLNumResultCols( m_hstmt, &wColumnCount );
if( m_retcode != SQL_SUCCESS ) // 列举结果集列的个数不成功
{
// 释放操作
…………
return;
}
LPSTR   pszName;
UWORD URealLength;
SWORD wColumnCount;   
UWORD wColumnIndex = 0;
SWORD wColumnType;   
UDWORD dwPrecision;     
SWORD wScale;         
SWORD wNullable;       
m_retcode = ::SQLDescribeCol( m_hstmt,
                          wColumnIndex, // 列的索引
                          pszName,    // 列的名称
                          256,    // 存放列名称的缓冲区大小
                          & nRealLength, // 实际得到列名称的长度
                          &wColumnType, // 列的数据类型
                          &dwPrecision, // 精度
                          &wScale, // 小数点位数
                          &wNullable ); // 是否允许空值
if(retcode != SQL_SUCCESS ) // 执行不成功
{
// 释放操作
…………
return;
}
retcode = ::SQLBindCol( m_hstmt,
                    uCounter, // 列索引
                    wColumnType, // 列数据类型
                    FieldValue, // 绑定的变量
                    dwBufferSize, // 变量内存大小
                    &BytesInBuffer); // 存放将来返回数据的大小的变量
if(retcode != SQL_SUCCESS ) // 执行不成功
{
// 释放操作
…………
return;
}
::SQLFetch( m_hstmt );
// 此后可以从绑定的变量里读取列的值。
…………
5.2.5 步骤5:提交事务
当所有的SQL语句都被执行并接收了所有的数据以后,应用程序需要调用SQLEndTran提交或者回退事务。如果提交方式为手工(应用程序设置)方式,则需要应用程序执行这个语句以提交或者回退事务,如果是自动方式,当SQL语句执行后,该命令自动执行。
事务是为了维护数据的一致性和完整性而设计的概念,事务要求:要么提交,将事务里包含的更新操作都提交到数据库里;要么会退,数据库恢复到事务前的状态,不会影响数据库的一致性和完整性。通常情况下,检索类SQL语句不涉及数据的更新,不会对数据的一致性和完整性产生影响,因此通常将检索类SQL语句设置提交方式为自动,而将更新类SQL语句的提交方式设置为手工方式,便于通过代码在事务处理中执行事务的提交或者会退,以维护数据库的一致性和完整性。在大型的商业应用中,这个设置非常有用。
调用SQLEndTran函数的语法如下:
:: SQLEndTran(SQL_HANDLE_DBC , hdbc, SQL_COMMIT); // 提交事务
:: SQLEndTran(SQL_HANDLE_DBC , hdbc, SQL_ROLLBACK); // 会退事务
5.2.6 步骤6:断开数据源连接并释放环境句柄
当应用程序使用完ODBC以后,需要使用SQLFreeHandle函数释放所有语句句柄、连接句柄、环境句柄。这里需要注意操作的顺序,应该是先释放所有语句句柄,调用SQLDisconnect函数解除与数据源的连接,然后释放所有连接句柄,最后释放环境句柄,使应用程序同ODBC管理器的连接彻底解除。
5.3 ODBC API编程实例
5.3.1 实例概述
需求调查与分析
某小规模图书公司为了对其出售图书进行日常管理,需要开发一个小型的数据库应用软件,通过使用这个软件,图书公司可以实现对其出售图书信息的登记、浏览、出售、删除和更新操作。
数据库系统及其访问技术
由于该公司营业规模较小,因此我们为这个应用选择了Microsoft Access数据库。在本节实现的数据库应用实例中,我们使用面向对象编程方法,通过对ODBC API进行封装,建立了一个可独立编译的数据库操纵软件包。该软件包实现了CODBCDatabase类和CODBCRecordset两个基本类,这两个基本类使用ODBC API,建立了进行数据库访问编程的各种操作方法,通过使用这两个类,数据库开发人员不仅可以详细了解ODBC API数据库编程的方法,更可以在这两个类的基础上,很方便地实现数据库应用软件的开发。
实例实现效果
ODBCDemo1是本书用于阐述ODBC API数据库编程的实例应用程序,该应用程序实现了某图书公司对出售图书的管理,包括信息登记、浏览、出售、删除和更新操作。应用程序运行界面如图5-1所示。

图5-1 ODBCDemo1实例应用程序的运行界面
5.3.2 实例实现过程
数据库设计
我们利用Microsoft Access工具设计本实例的数据库结构。在本实例里,我们需要利用数据库存放图书的如下信息:
" 图书基本信息:包括图书的书号(ISBN)、书名、出版商、出版日期、作者、价格。
" 图书类别信息:我们把图书分成了科学技术、语言文学、政治经济、历史地理、意识形态和艺术等类别,每个类别有不同的类别代码。
" 图书出售信息:包括图书出售日期、出售价格、出售数量。
为此我们为数据库设计了四个表,表"基本信息"存放图书的基本信息,表"出售信息"存放图书的出售信息,表"类别信息"存放图书的类别信息。为了便于数据访问,我们还定义了一个视图"出售图书",该视图管理了已经出售图书的全部信息。
表5-1列出了表"基本信息"的结构,表5-2列出了表"出售信息"的结构,表5-3列出了表"类别信息"的结构。
表5-1 表"基本信息"的结构
字段名称 类型 字段名称 类型
图书ID(key) 自动编号 价格 货币
标题 文本 作者 文本
版权日期 数字 类别ID 数字
ISBN 文本 出版商 文本
表5-2 表"出售信息"的结构
字段名称 类型 字段名称 类型
图书ID 自动编号 ISBN 文本
出售价格 数字 出售数量 数字
表5-3 表"类别信息"的结构
字段名称 类型 字段名称 类型
类别ID(key) 自动编号 类别名称 文本
在实例光盘的Database目录下,book.mdb文件是存放图书信息的Access数据库文件,读者可以查看这个文件,了解这个数据库的详细信息。
创建ODBCDemo1工程
ODBCDemo1工程是一个基于对话框的应用程序,创建应用程序工程时需要选择基于对话框的应用程序类型。
创建ODBCDemo1工程的操作步骤如下:
(1) 打开VC++的工程创建向导。从VC++的菜单中执行"File>New"命令,将VC++ 6.0工程创建向导显示出来。如果当前的选项标签不是Project,单击Project选项标签将它选中。在左边的列表里选择MFC AppWizard(exe)项,在Project Name编辑区里输入工程名称"ODBCDemo1",并在Location编辑区里调整工程路径,如图5-2所示。
(2) 选择应用程序的框架类型。在图5-2中,单击"工程创建向导"窗口的OK按钮,开始创建ODBCDemo1工程。创建ODBCDemo1工程的第一步是选择应用程序的框架类型。如图5-3所示。弹出的"MFC AppWizard - Step 1"对话框里,选择"Dialog Based",保持资源的语言类型为"中文",单击"Next >"按钮,执行第二步。


图5-2 工程创建向导

图5-3 选择应用程序的框架类型
(3) 设置应用程序支持的特性。在弹出的"MFC AppWizard - Step 2 of 4"对话框里,设置如下三项:
" About box
" 3D Controls
" ActiveX Controls
在编辑区里输入"ODBC API编程实例 - 图书管理",作为应用程序对话框的标题,如图5-4所示。

图5-4 设置应用程序支持的特性
(4) 选择工程风格和MFC类库的加载方式。在"MFC AppWizard - Step 2 of 4"对话框里,单击"Next >"按钮,弹出"MFC AppWizard - Step 3 of 4"对话框。在对话框里设置如下三项:
" MFC Standard
" Yes, Please
" As a Shared DLL
如图5-5所示,单击"Next >"按钮,进入下一步。

图5-5 选择工程风格和MFC类库的加载方式
(5) 显示工程创建信息。在本例中,ODBCDemo1工程包含了两个类:
" CODBCDemo1App类,工程的应用类。
" CODBCDemo1Dlg类,工程主对话框类。
这两个类构成了应用程序工程的主要框架。在"MFC AppWizard - Step 3 of 4"对话框里单击Finish按钮,工程创建向导将该次工程创建的信息显示在一个对话框里,如图5-6所示。在对话框里单击"OK"按钮,ODBCDemo1工程创建完成。

图5-6 工程创建信息
创建数据源
为了演示应用程序对数据源的操作,该实例提供了一个名称为"Authors.mdb"的Access数据库,应用程序的所有操作将在该数据库上实施。通过第1章对ODBC的介绍我们知道,要通过ODBC API实现对数据库的操作,必须为数据库连接创建数据源。下面详细叙述ODBC数据源的创建过程。
操作步骤:
(1) 打开ODBC数据源管理器。如果使用的是Windows 98操作系统,需要在控制面板里双击"数据源 (ODBC)"图标,打开ODBC数据源管理器;如果使用的是Windows 2000(家族)操作系统,需要在控制面板里双击"管理工具"图标,然后在管理工具里双击"数据源 (ODBC)"图标,打开ODBC数据源管理器,如图5-7所示。

图5-7 ODBC 数据源管理器
(2) 创建ODBCDemo1数据源。在数据源管理器里单击"添加"按钮,弹出"创建新数据源"对话框,开始创建ODBCDemo1数据源,如图5-8所示。首先选择数据源驱动程序,在列表里,选择"Microsoft Access Driver(*.mdb)"项。

图5-8 为要创建新的数据源选择驱动程序
(3) 配置创建的新数据源。在"创建新数据源"对话框里单击"完成"按钮,弹出"ODBC Microsoft Access安装"对话框,在对话框里配置创建的新数据源。如图5-9所示,输入数据源名称"ODBCDemo1",在说明编辑区里输入"Data source for ODBC API programming.",单击"选择"按钮,选择要关联的Microsoft Access数据库(*.mdb),在本例里,我们选择Databases目录下的book.mdb文件,保持其它设置。

图5-9 "ODBC Microsoft Access安装"对话框
(4) 确认并创建数据源。在"ODBC Microsoft Access安装"对话框里单击"确定"按钮,完成ODBCDemo1数据源的创建,并返回ODBC数据源管理器,数据源管理器显示了刚才创建的ODBCDemo1数据源,如图5-10所示。
(5) 单击"确定"按钮,完成数据源创建。
设计应用程序界面
在上一节,我们利用工程创建向导创建了一个基于对话框的工程,该工程自动将IDD_ODBCDEMO1_DIALOG对话框添加到工程资源里,我们需要对该对话框进行重新设计,以实现应用程序的操作入口。

图5-10 创建了ODBCDemo1数据源的ODBC数据源管理器
设计IDD_ODBCDEMO1_DIALOG对话框的操作步骤如下:
(1) 打开IDD_ODBCDEMO1_DIALOG对话框。如果VC++的工程工作区窗口没有显示,应该先打开该窗口,执行"View>Workspace"菜单命令,或者按下【Alt】+0快捷键,将VC++的工程工作区窗口显示出来。在工程工作区窗口底部单击"Resource"选项标签,显示"工程资源"选项标签,如图5-11所示。在窗口里双击IDD_ODBCDEMO1_DIALOG项,打开IDD_ODBCDEMO1_DIALOG对话框,如图5-12所示。

图5-11 "工程资源"选项标签

图5-12 初始的IDD_ODBCDEMO1_DIALOG对话框
(2) 设计IDD_ODBCDEMO1_DIALOG对话框。在IDD_ODBCDEMO1_DIALOG对话框上添加表5-4所列出的资源。
表5-4 IDD_ODBCDEMO1_DIALOG对话框的资源
资源类型 资源ID 标题 功能
编辑框 IDC_TITLE   显示书的标题信息
编辑框 IDC_AUTHOR   显示书的作者信息
编辑框 IDC_ISBN   显示书号
编辑框 IDC_PUBLISHER   显示书的出版商信息
编辑框 IDC_PRICE   显示书的单价
编辑框 IDC_YEARPUBLISH   显示书的出版日期
组合框 IDC_STATIC 信息: 信息容器的标题
组合框 IDC_STATIC 操作 操作容器标题
按钮 IDC_NEXT >> 下一条记录
按钮 IDC_PREVIOUS << 上一条记录
按钮 IDC_END >| 最后一条记录
按钮 IDC_HOME |< 第一条记录
按钮 IDC_INSERT 插入 插入记录操作按钮
按钮 IDC_DELETE 删除 删除记录操作按钮
按钮 IDC_UPDATE 更新 更新记录操作按钮
按钮 IDC_CONNECT 启动连接 启动连接操作按钮
按钮 IDC_DISCONNECT 断开连接 断开连接操作按钮
按钮 IDCANCEL 退出 退出程序操作按钮
标签 IDC_STATIC 书名: 书名标签
标签 IDC_STATIC 作者: 作者标签
标签 IDC_STATIC 书号: 书号标签
标签 IDC_STATIC 出版时间: 出版时间标签
标签 IDC_STATIC 出版商: 出版商标签
标签 IDC_STATIC 单价: 单价标签
标签 IDC_STATIC Info 信息标签
(3) 保存资源。按下【Ctrl】+【S】快捷键,保存资源修改,完成后IDD_ODBCDEMO1_DIALOG对话框如图5-13所示。
编写工程代码
为了便于介绍ODBC API编程的方法和技巧,ODBCDemo1工程特别以面向对象的方式建立了一个CODBCDatabase类和一个CODBCRecordSet类。
我们首先介绍CODBCDatabase类和CODBCRecordSet类的功能与实现方法,然后介绍使用这两个类建立一个基于ODBC API的数据库应用程序。由于CODBCDatabase类和CODBCRecordSet类用到了一些全局常量、宏以及结构,我们首先创建这些内容。

图5-13 设计完成后的IDD_ODBCDEMO1_DIALOG对话框
1. 创建全局常量、宏以及数据结构
DECLARATIONS_FOR_ODBC.h文件包含了所有的全局常量、宏以及数据结构。创建全局变量、宏以及数据结构的操作步骤如下:
(1) 在DECLARATIONS_FOR_ODBC.h文件里引入下面的头文件:
   #include "sqltypes.h"
   #include "sql.h"
   #include "sqlext.h"
上述的头文件包含了所有SQL数据类型、函数、扩展函数的声明。
(2) 添加下面的全局变量:
// Constants ===========================
#define MAX_FIELDS 20 // 表示最大的字段数的常量
// 应用程序运行状态常量
#define ODBC_DISPLAY_ERRORINFO_IN_DEBUG    0x00000001
#define ODBC_DISPLAY_ERRORINFO_IN_RELEASE 0x00000002
#define ODBC_DISPLAY_ERRORINFO_IN_FILE     0x00000004

#define ODBC_EXCEPTION_SET_STMT_OPTION 100
// 表示ODBC异常发生时的移动操作的常量
#define ODBC_EXCEPTION_MOVE_ABSOLUTE   10
#define ODBC_EXCEPTION_MOVE_RELATIVE    20
#define ODBC_EXCEPTION_MOVE_BOOKMARK 30
#define ODBC_EXCEPTION_MOVE_NEXT        40
#define ODBC_EXCEPTION_MOVE_PREVIOUS    50
#define ODBC_EXCEPTION_MOVE_LAST        60
#define ODBC_EXCEPTION_MOVE_FIRST       70
// ODBC状态常量
#define ODBC_HSTMT_ALLOCATED       0x00000001
#define ODBC_BUFFERS_ALLOCATED     0x00000002
#define ODBC_BOOKMARKS              0x00000004
#define ODBC_VALID_CURSOR           0x00000008
#define ODBC_CURSOR_OPEN            0x00000010
#define ODBC_NO_RECORDS              0x00000020
// 初始化方式
#define ODBC_INITIALIZE_COLUMN_NAME    0x00000001
#define ODBC_INITIALIZE_BIND              0x00000002
// SQL语句执行方式
#define ODBC_EXEC_DIRECT                  1
#define ODBC_EXEC_PREPARE                 2
#define ODBC_EXEC_PREPARE_AND_EXECUTE    3
(3) 添加数据结构,包括:
" ODBC_OPTION:用于设置连接或者语句选项
" ODBC_BIND_PARAMETER:用于列绑定的数据结构
" ODBC_DATA_AT_EXECUTION:用于运行时填充列的数据结构
" ODBC_GET_DATA:用于读数据的数据结构
这些数据结构的代码如下:
// Structures ========================
typedef struct tagODBC_OPTION
{
   UWORD m_wOption;
   UDWORD m_dwParam;
} ODBC_OPTION, * PODBC_OPTION;

typedef struct tagODBC_BIND_PARAMETER
{
   HSTMT   m_hstmt;      // 连接句柄
   UWORD   m_wIndex;      // 绑定参数的索引
   SWORD   m_wParamType; // 参数类型
   SWORD   m_wCType;      // C类型参数
   SWORD   m_wSQLType; // SQL数据类型
   UDWORD m_dwPrecision;   // 精度
   SWORD   m_wScale;      // 小数点位数
   PTR        m_pBuffer;    // 存放数据的缓存
   SDWORD m_dwMaxBytes; // 缓存容量
   SDWORD* m_pdwBytesRetrieved;     // 实际数据大小
} ODBC_BIND_PARAMETER, * PODBC_BIND_PARAMETER;

typedef struct tagODBC_DATA_AT_EXECUTION
{
   short    m_sParameterIndex;       // 数据传输参数的索引
   UINT     m_uBufferLength;      // 缓存大小
   void*    m_pBuffer;              // 缓存指针
} ODBC_DATA_AT_EXECUTION, * PODBC_DATA_AT_EXECUTION;

typedef struct tagODBC_GET_DATA
{
   UWORD   wColumn;     // 数据获取的列索引
   SWORD   wCType;      // C类型
   PTR     pBuffer;        // 缓存指针
   SDWORD dwMaxBytes; // 缓存容量
   SDWORD dwByteRead; // 实际数据大小
} ODBC_GET_DATA, * PODBC_GET_DATA;
(4) 添加宏。定义宏的目的是便于编程时对代码的灵活使用。本实例定义了错误检查、参数绑定、数据复制和获取操作的宏。这些宏的源代码如下:
// Macros ==============================
#ifdef _DEBUG // 在DEBUG状态下将字符串作为异常发出
   #define CHECK(text) \             
      if( CheckODBCError() == FALSE ) throw text;
#else   // 而在其他状态时发出异常为空字符串
   #define CHECK(text) \
      if( CheckODBCError() == FALSE ) throw (char*)NULL;  
#endif
#define CHECK_FOR_ERROR() CheckODBCError();
#define CHECK_AND_BREAK() \
   if( CheckODBCError() == FALSE ) return FALSE;
#define CHECK_AND_THROW() \
   if( CheckODBCError() == FALSE ) throw 0;  
// 执行绑定参数操作
#define ODBC_BIND_PARAMETER( par, hstmt, index, type, c_type, sql_type,
                                prec, buff, max, read ) \
                   par.m_hStmt = hstmt;   
                   par.m_wIndex = index;
                   par.m_wParamType = type;  
                   par.m_wCType = c_type;
                   par.m_wSQLType = sql_type;   
                   par.m_dwPrecision = prec;
                   par.m_wScale = 0;      
                   par.m_pBuffer = buff;        
                   par.m_dwMaxBytes = max;  
                   par.m_pdwBytesRetrieved = read;
// 执行绑定二进制参数操作
#define ODBC_BIND_PARAMETER_BINARY( par, index, type, byte_count,
                                         buffer, read ) \
                   par.m_hstmt = SQL_NULL_HSTMT;
                   par.m_wIndex = index;              
                   par.m_wParamType = type;  
                   par.m_wCType = SQL_C_BINARY;
                   par.m_wSQLType = SQL_LONGVARBINARY;
                   par.m_dwPrecision = byte_count;
                   par.m_wScale = 0;           
                   par.m_pBuffer = buffer;            
                   par.m_dwMaxBytes = byte_count + 1;  
                   par.m_pdwBytesRetrieved = read;    
// 执行数据赋值操作
#define ODBC_DATA_AT_EXECUTION( data, index, length, buffer ) \
                   data.m_sParameterIndex = index;
                   data.m_uBufferLength = length;
                   data.m_pBuffer = buffer;
// 执行数据获取操作
#define ODBC_GET_DATA( data, column, ctype, buffer, max ) \
                   data.wColumn = column;
                   data.wCType = ctype;
                   data. pBuffer = buffer;
                   data.dwMaxBytes = max;

posted on 2011-01-17 10:40  cy163  阅读(...)  评论(...编辑  收藏

导航