 | 级别: 初级 Rajesh Kartha (kartha@us.ibm.com), 软件工程师, IBM Cloudscape Group
2004 年 11 月 01 日 本文将向您介绍使用 Apache Derby 或 IBM® Cloudscape™ 作为后台数据源的 ODBC 编程。您将学习 ODBC 应用程序与嵌入式 SQL 应用程序的不同之处,还将学习如何将 Apache Derby 设为 ODBC 数据源,以及 ODBC 应用程序的结构是怎样的。此外,作者还谈到了错误处理,并提供了一个例子。
嵌入式 SQL 与 ODBC
Open Database Connectivity (ODBC) 是目前编写数据库应用程序所使用的一种最流行的方式。为了研究用于 Apache Derby 的 ODBC
编程,首先我们将 ODBC 应用程序与嵌入式 SQL 应用程序作一番比较。
嵌入式 SQL 应用程序包含嵌入到源代码中的 SQL 语句,源代码是用高级语言编写的,常见的例子是用 C 语言。相反,ODBC 应用程序使用一套标准的 API 来与数据库通信。SQL 语句通过这些标准 API 方法发送到数据库管理器,让数据库管理器加以处理。
嵌入式 SQL 与 ODBC 应用程序之间的一些主要不同是:
-
嵌入式 SQL 应用程序易缺乏互操作性。当需要使用其他 RDBMS 时,可能不得不修改应用程序。
-
在 ODBC 应用程序中,每当执行 SQLExecute()/SQLExecDirect() 函数时,如果需要游标,那么可以自动生成游标。相反,嵌入式 SQL 要求使用一条 DECLARE CURSOR 语句。
-
ODBC 应用程序不需要显式声明,也不需要使用宿主变量。
-
ODBC 函数使用句柄管理环境、连接和 SQL 信息。因此,不需要复杂的特定于产品的数据结构(例如用于 DB2 嵌入式 SQL 应用程序的 SQLCA 和 SQLDA)。
-
通常,在一个手动的事务中,ODBC/CLI 应用程序依靠标准 SQLEndTran() 函数来结束活动的事务。而嵌入式 SQL 应用程序则依赖于动态准备 SQL 语句时所使用的方式,这里使用的方式对于不同数据库供应商可能有所不同。
由于 ODBC 应用程序将所有 SQL 语句传给数据源来执行,因此很容易获得应用程序的可移植性。而对于嵌入式 SQL 应用程序,这可能是个问题。
DB2 Runtime Client
对 Apache Derby 或 IBM Cloudscap 的 ODBC 支持是通过 DB2 Runtime Client 来达到的。分布式关系数据库体系结构(distributed relation database architecture,DRDA)是用于客户机与服务器之间通信的常见协议,它由 Runtime Client 提供。请参阅 developerWorks 文章
Cloudscape 和 ODBC 中提供的说明,以了解更多关于安装说明和平台支持的信息。
将 Apache Derby 数据库设为 ODBC 数据源
要为 Apache Derby 数据库创建一个 ODBC 数据源,需遵循以下步骤:
-
启动 Apache Derby Network Server。请参阅 Derby 文档,以了解有关设置 CLASSPATH 和各种用来启动 Derby Network Server 的选项的信息。
-
例如:
java org.apache.derby.drda.NetworkServerControl start
上述命令将使用默认选项启动 Derby Network Server:主机是 'localhost',端口是 '1527'。
-
使用 'ij' 工具连接到 Derby Network Server,以创建数据库。
-
例如:
ij> connect 'jdbc:derby:net://localhost:1527/SAMPLE;create=true:user=app;password=app;';
上述命令连接到 Derby Network Server 并创建一个名为 SAMPLE 的数据库。
-
通过 CLP,用 DB2 Runtime client 编目 Derby Network Server。
-
db2 catalog tcpip node DERBYNET remote localhost server 1527
-
通过命令行处理器(CLP),用 DB2 Runtime client 编目数据库。
-
db2 catalog db SAMPLE at node DERBYNET authentication server
-
使用 DB2 CLP 验证到数据库的连接:
-
db2 connect to SAMPLE user abc using abc
-
创建 ODBC 数据源:
-
在 Windows® 2000 上,通过单击
Start -> Settings ->
Control Panel -> Administrative Tools -> Data Sources -> System DSN 创建 ODBC 数据源。
-
对于 Windows,另一种方法是使用 DB2 CLP 来执行 ODBC 数据源的创建。
-
db2 catalog system odbc data source SAMPLE
-
用下面的命令可以列出当前具备的一组 ODBC 数据源:
-
db2 list system odbc data sources
-
对于 Linux,这要取决于安装的 ODBC Driver Manager 的类型。请参阅其文档中关于设置 ODBC 数据源的部分。

 |

|
ODBC 应用程序的结构
ODBC 和 DB2 Call Level Interface (CLI) 有很多方法调用是相同的。但是,在 ODBC 中提供了一些额外的与驱动程序管理器相关的方法调用。要了解更多关于 ODBC 的信息,请参阅
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/odbc/htm/odbcabout_this_manual.asp.。
ODBC 应用程序与 CLI 应用程序之间主要的不同在于装载数据库驱动程序的方式。在 ODBC 环境中,Driver Manager 为应用程序提供接口。它还动态地为应用程序所连接到的数据库服务器装载必需的驱动程序。应用程序对 ODBC DriverManager 进行的每个 ODBC 函数调用都被转到适当的数据源驱动程序进行处理。在 CLI 环境中,应用程序将自己装载数据库驱动程序,因此只能与一种数据源驱动程序绑定在一起。
图 1 解释了这个不同点。
图 1. CLI 与 ODBC 环境中间的不同点
要了解更多关于 DB2 CLI 的信息,请参阅 DB2 CLI Guide and
Reference,
卷 1 和
卷 2。
一个 ODBC 应用程序主要由以下三部分组成:
-
初始化
-
事务处理
-
终止
1) 初始化
在这个阶段中,要装载创建连接、执行 SQL 和执行事务所需的所有资源,如
图 2 所示。
图 2. ODBC 环境中的初始化
在 ODBC 中,所有这些资源都是一些指向数据对象的指针(数据存储区域),它们由 ODBC Driver Manager 或 DB2
CLI 控制,被称为‘句柄’。主要有 4 种类型的句柄:
表 1. ODBC 中的句柄类型
|
句柄类型
|
目的
| |
环境(environment)
|
含有特定于 ODBC 信息的全局存储
| |
连接(connection)
|
用于数据源连接的存储
| |
语句(statement)
|
用于单个 SQL 语句的存储
| |
描述符(descriptor)
|
用于元数据的存储,含有关于在绑
定到参数标记或结果集的列时使用
的应用程序变量的信息
|
环境句柄
分配一个环境句柄通常是 ODBC 应用程序中的第一步。为了在应用程序中分配其他类型的句柄,必须提供一个环境句柄。环境句柄是在指定了 SQL_HANDLE_ENV 选项的情况下,通过调用 SQLAllocHandle() 函数分配的。
SQLHANDLE EnvHandle = 0;
SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &EnvHandle);
|
连接句柄
要为 ODBC 应用程序建立与一个数据源的连接,首先要创建第一个连接句柄。连接句柄是在指定了 SQL_HANDLE_DBC 选项的情况下,通过调用 SQLAllocHandle() 函数来分配的。
SQLHANDLE ConnHandle = 0;
SQLAllocHandle(SQL_HANDLE_DBC, EnvHandle, &ConnHandle);
|
要建立到一个数据源的连接,可以使用下面的某一种方法:
表 2. 用于连接到一个数据源的方法
|
函数调用
|
用法
| |
SQLConnect()
|
这是最简单的连接函数,只需要
一个数据源名,
用户名和口令。所有其他需要的信息应该在
db2cli.ini 或 ODBC.INI 文件中
| |
SQLDriverConnect()
|
通过使用这个函数,应用程序可以使用一个连接字符串将连接信息发送到数据源驱动程序,
连接字符串带有键-值对,中间以分号隔开。这种方法不要求在
db2cli.ini 或 ODBC.INI 文件中存储任何信息
例如:DSN=SAMPLE;UID=app;PWD=app;
| |
SQLBrowseConnect()
|
该函数类似于 SQLDriverConnect() 函数,它
使用连接字符串将连接信息发送到驱动程序。但是,通过
使用 SQLBrowseConnect() 函数,可以在应用程序运行时
构造一个完整的连接字符串,例如使用用户通过应用程序的对话框提供的连接信息
|
语句句柄
语句句柄是最重要的一种句柄,因为要用它在应用程序中处理 SQL 语句。为了执行每条 SQL 语句,需要有一条与该语句相关的语句句柄,因此一个应用程序可以有多个语句句柄。语句句柄是在指定了 SQL_HANDLE_STMT 选项的情况下, 通过调用 SQLAllocHandle() 函数来分配的。
SQLHANDLE StmtHandle = 0;
SQLAllocHandle(SQL_HANDLE_STMT, ConHandle, &StmtHandle);
|
语句句柄可用于:
-
将应用程序变量绑定到预备语句中的参数标记。
-
准备和提交用于执行的语句。
-
获得关于所产生的结果数据集的元数据。
-
将应用程序变量与所产生的结构数据集中的列绑定
-
从产生的结果数据集检索(取)数据。
-
在语句失败时获得诊断信息。
描述符句柄
在分配每个语句句柄的同时,还会自动分配 4 个与之相关的描述符句柄。这些描述符句柄一直与相应语句句柄相关联,直到被销毁。ODBC 应用程序可以使用这些隐式定义的描述符句柄执行大多数操作。
在指定了 SQL_HANDLE_DESC 选项的情况下,通过调用 SQLAllocHandle() 函数可以显式地分配一个或多个描述符句柄,以及一个有效的连接句柄和语句句柄。
属性
ODBC 应用程序可以连接到多种不同的数据源,并且可以使用 SQLGetInfo()、SQLGet函数() 和 SQLGetTypeInfo() 函数来获取关于正在使用的底层数据源驱动程序的更多信息。此外,还可以通过修改一些‘驱动程序属性’来更改驱动程序的行为。下面是可用的一些类型的驱动程序属性:
请注意,下面在每种类型的驱动程序属性之下提到的属性都是 ODBC 应用程序中最常见的一些属性。要获得关于每种类型属性的完整列表和描述,请参阅
DB2 CLI Guide and Reference, Volume 2。
在编写本文之际,Apache Derby 所支持的 ODBC 属性的列表还没有完成。当它们完成时,
developerWorks 上将给出一份完整的列表。
环境属性
这些属性将影响特定环境下 ODBC 函数的行为方式。环境属性只有在还没有使用环境句柄分配连接句柄的情况下才能改变,一旦分配了连接句柄,就只能检索这些属性,而不能改变它们。用于访问这些属性的方法是 SQLSetEnvAttr()/SQLGetEnvAttr()。
表 3. 环境属性
|
SQL_ATTR_ODBC_VERSION
|
设置要使用的 ODBC 版本
| |
SQL_ATTR_OUTPUT_NTS
|
驱动程序是否需要将 null 终止符附加到字符串数据后面
|
连接属性
这些属性将影响到数据源的连接行为。这些连接属性可以在不同的时候设置,具体情况取决于每种属性:在分配连接句柄之前/之后,或在建立与数据源的连接之前/之后。用于访问这些属性的函数是 SQLSetConnectAttr()/SQLGetConnectAttr()。
表 4. 连接属性
|
SQL_ATTR_AUTOCOMMIT
|
自动提交开/关
| |
SQL_ATTR_MAXCONN
|
可以为应用程序打开的最大并发连接数
| |
SQL_ATTR_TXN_ISOLATION
|
为当前连接设置隔离级别
SQL_TXN_SERIALIZABLE 用于可重复读,
SQL_TXN_REPEATABLE_READ 用于读稳定性,
SQL_TXN_READ_COMMITTED 用于游标稳定性,
SQL_TXN_READ_UNCOMMITTED 用于未提交的读操作
|
语句属性
这些属性将影响语句级函数的行为。同样,这些语句属性可以在不同的时候设置,具体情况取决于每个属性:在执行 SQL 语句之前/之后或任何时候。用于访问这些属性的函数是
SQLSetStmtAttr()/SQLGetStmtAttr()。
表 5. 语句属性
|
SQL_ATTR_CURSOR_SENSITIVITY
|
使其他游标作出的改变可见或不可见
| |
SQL_ATTR_CURSOR_TYPE
|
游标的类型(单向的、静态的、键集驱动的(keyset-driven)或动态的)
| |
SQL_ATTR_CONCURRENCY
|
指定要使用的游标并发级别(只读、低级、
锁,或者值比较锁)
| |
SQL_ATTR_RETRIEVE_DATA
|
指定 SQLFetch() 和 SQLFetchScroll() 函数
是否在定位到游标时自动检索数据
|
2) 事务处理
在初始化了所有必需的句柄之后,将在 ODBC 应用程序接下来的这一部分中,处理所有的事务,将 SQL 语句发送到数据源,并处理结果。在
图 3 中可以看到,这里设计的主要步骤有:
-
分配一个或多个语句句柄(在关于 ODBC 应用程序的结构的
上一节曾讲到)。
-
执行 SQL 语句。
-
处理结果。
-
管理事务
图 3. ODBC 应用程序中的事务处理
Executing SQL 语句
有两种执行 SQL 语句的方法:Prepare and Execute(准备并执行)以及 Execute Immediate(直接执行)。
Prepare and Execute
当语句需要重复执行时,一般使用这种方法。语句可以有多个参数标记('?')。这种方法使用 ODBC 函数 SQLPrepare() 和 SQLExecute() 来处理 SQL 语句。
SQLPrepare() 函数准备语句:
strcpy((char *) SQLStmt, "INSERT INTO EMPLOYEE VALUES (?,?,?,?, CURRENT DATE)" );
RetCode = SQLPrepare(StmtHandle, SQLStmt, SQL_NTS);
|
其中 '?' 表示代入应用程序变量值的位置。可以使用 SQLBindParameter() 函数将应用程序变量与参数标记绑定,并使之关联。一旦应用程序变量被绑定到某个参数标记,这种关联就会一直保持下去,直到相应的语句句柄被释放。只有在 SQL 语句的执行期间才会检索绑定的标量。
SQLExecute() 函数执行语句:
RetCode = SQLBindParameter(StmtHandle, 1,SQL_PARAM_INPUT, SQL_C_LONG, \
SQL_INTEGER,sizeof(EmpId), 0, &EmpId ,sizeof(EmpId), NULL);
RetCode = SQLExecute(StmtHandle);
|
Execute Immediate
这种方法通常在语句只需要执行一次时使用。这里使用 ODBC 函数 SQLExecDirect() 来处理 SQL 语句。
SQLExecDirect() 方法实施语句的执行:
strcpy((char *) SQLStmt, "Create table Employee(empno int, firstname char(15), \
lastname char(25), jobtype varchar(10), date date) " );
RetCode = SQLExecDirect(StmtHandle, SQLStmt, SQL_NTS);
|
处理结果
必须检索和处理由 SQL 语句生成的结果。这些结果存储在一些存储区域中,您可以使用执行期间用到的连接和语句句柄来引用结果。对于没有生成任何结果的语句,可以使用函数返回代码(return code)来验证语句的执行是否成功。为了在 SQL 语句生成的结果集中检索行,需要执行以下步骤:
-
对于所执行的 SQL 语句,通过使用 SQLNumResultCols()、SQLDescribeCol() 和 SQLColAttributes() 函数获得结果集的结构,包括列类型、各列的数据长度等。
-
使用 SQLBindCol() 函数将应用程序变量绑定到结果集中的列。这些变量被用作输出参数,在执行取(fetch)操作时将检索值并将该值赋给这些变量。
SQLBindCol(StmtHandle, 1, SQL_C_CHAR, (SQLPOINTER)EmpNo, sizeof(EmpNo), NULL);
|
-
使用 SQLFetch() 和 SQLGetData() 方法的任何组合检索存储在结构数据集中的数据。您可以通过重复调用 SQLFetch() 函数,将结果集中的值读取到绑定的应用程序变量中,直到没有可用的数据为止。如果没有绑定变量,那么可以通过使用 SQLGetData() 函数来获得值。
事务管理
事务是一个工作单元,由一组 SQL 查询组成,这些 SQL 要么全部提交,要么全部回滚。对于事务处理,有两种提交模式:
- 自动提交(auto
commit)
- 手动提交(manual commit)
对于 ODBC 应用程序,默认情况下,提交模式是‘自动提交’,在这种模式下,每条独立的 SQL 语句都被当作一个完整的事务,在它成功执行后就会被自动提交。为了使用‘手动提交’,您可以使用下面的语句关闭自动提交:
SQLSetConnectAttr(ConHandle, SQL_ATTR_AUTOCOMMIT,SQL_AUTOCOMMIT_OFF, SQL_IS_UINTEGER);
|
在手动提交模式下,事务从第一条语句被执行时开始,并在调用 SQLEndTran() 函数时显式地结束。为了提交一个事务,可以调用带有 SQL_COMMIT 选项的 SQLEndTran() 函数,如下所示:
SQLEndTran(SQL_HANDLE_DBC, ConHandle,SQL_COMMIT);
|
为了回滚一个事务,可以调用带 SQL_ROLLBACK 选项的 SQLEndTran() 函数:
SQLEndTran(SQL_HANDLE_DBC, ConHandle,SQL_ROLLBACK);
|
3) 终止
当应用程序中的所有 SQL 语句都执行完毕,没有剩余时,必须完全释放在事务期间分配的存储区域/内存。您可以使用 SQLFreeHandle() 函数来释放这些空间,在执行这个函数时,所有与参数和列绑定的应用程序变量都被解除绑定,所有打开的游标都被关闭,与语句句柄和描述符句柄相关的内存都被释放。
SQLFreeHandle(SQL_HANDLE_STMT, StmtHandle);
|
类似地,如果要终止应用程序,需要终止所有连接,并且需要释放为连接句柄和环境句柄分配的所有资源。为了断开已建立的连接,可以使用 SQLDisconnect() 函数,如下所示:
SQLDisconnect(ConHandle);
|
通过分别调用带 SQL_HANDLE_DBC 和 SQL_HANDLE_ENV 选项的 SQLFreeHandle() 函数,释放用于连接句柄和环境句柄的存储区域。
SQLFreeHandle(SQL_HANDLE_DBC, ConHandle); //free the connection handle
SQLFreeHandle( SQL_HANDLE_ENV, EnvHandle); //free the environment handle
|
错误处理和诊断
返回代码和 SQLSTATE 消息可以帮助诊断 ODBC 应用程序执行期间出现的错误状况。
返回代码
对于每个被调用的 ODBC 函数,都要向调用它的应用程序返回一个值,该值就是所谓的返回代码。返回代码指出函数是否如预期的那样执行。
表 6 列出了 ODBC 应用程序可能返回的返回代码:
表 6. ODBC 返回代码
|
SQL_SUCCESS_WITH_INFO
|
函数成功完成,带有警告/非重大错误
| |
SQL_NO_DATA or SQL_NO_DATA_FOUND
|
函数成功完成,没有任何数据
| |
SQL_NEED_DATA
|
函数失败,没有数据(连接信息或参数数据)
| |
SQL_INVALID_HANDLE
|
函数失败,有无效的句柄
| |
SQL_STILL_EXECUTING
|
有一个异步的函数仍然在进行中
| |
SQL_ERROR
|
函数失败
| |
SQL_SUCCESS
|
函数成功完成
|
SQL 状态
SQLSTATE 消息可以提供关于导致错误或警告状况的原因的特定信息,这对于解决错误很有用。几乎所有的数据库产品都使用 SQLSTATE 来提供这样的信息。SQLSTATE 是字母和数字组成的字符串,长度为 5 个字符(字节),格式为 'ccSSS',其中 'cc' 指出了错误消息的类型,如下所示:
|
01
|
警告
| |
HY
|
DB2 Call Level Interface 错误
| |
IM
|
ODBC Driver Manager 错误
|
'SSS' 指出了错误消息子类。
注意,Apache Derby SQLSTATE 可能会随不同的 DB2 SQLSTATE 而有所不同。
获取诊断信息
可以通过调用 SQLGetDiagRec() 函数、SQLGetDiagField() 函数或者同时调用这两个函数来获得 SQLSTATE、诊断消息和本地错误代码信息。
这些函数以环境句柄、连接句柄、语句句柄或描述符句柄作为输入参数,返回与上一次使用该句柄作出的函数调用相关的诊断信息。对于生成的多条诊断记录,带记录数 0 和 SQL_DIAG_NUMBER 选项的 SQLGetDiagField() 函数返回记录的总数。对于每条诊断记录,SQLGetDiagRec() 函数调用将检索 SQLSTATE 值、诊断消息和本地错误代码。
一个例子
上一节描述了 ODBC 应用程序中的各种不同模块。
清单 1 展示了一个简单的 ODBC 应用程序,该应用程序是用 C 语言为 Windows 平台编写的,它首先与一个名为 'SAMPLE' 的 ODBC 数据源建立连接,然后将一行记录插入到 EMPLOYEE 表中,最后检索所有工作名(job title)为 'ENGINEER' 的雇员的雇员 id 和姓氏。
方法 print_error() 帮助诊断执行期间的错误状况,并显示 SQLGetDiagRec() 和 SQLGetDiagField() 函数的使用情况。
示例 ODBC 程序必须用一个适当的 C 语言编译器编译,例如 Visual C++。
Listing 1. Example ODBC 应用程序
#include <stdio.h>
#include <windows.h>
#include <sql.h>
#include <sqlext.h>
#include <sqltypes.h>
void print_error( SQLSMALLINT htype, /* A handle type identifier */
SQLHANDLE hndl, /* A handle */
SQLRETURN frc, /* Return code to be included with error msg */
int line, /* Used for output message, indcate where */
char * file /* the error was reported from */
) {
SQLCHAR buffer[SQL_MAX_MESSAGE_LENGTH + 1] ;
SQLCHAR sqlstate[SQL_SQLSTATE_SIZE + 1] ;
SQLINTEGER sqlcode ;
SQLSMALLINT length, i ;
SQLINTEGER NumRecords;
printf( ">--- ERROR -- RC = %d Reported from %s, line %d ------------\n",
frc,
file,
line
) ;
SQLGetDiagField(htype, hndl, 0,SQL_DIAG_NUMBER, &NumRecords, SQL_IS_INTEGER,NULL);
printf("Total Number of diagnostic records: %d\n",NumRecords);
i = 1 ;
while ( SQLGetDiagRec( htype,
hndl,
i,
sqlstate,
&sqlcode,
buffer,
SQL_MAX_MESSAGE_LENGTH + 1,
&length
) == SQL_SUCCESS ) {
printf( " SQLSTATE: %s\n", sqlstate ) ;
printf( "Native Error Code: %ld\n", sqlcode ) ;
printf( "%s \n", buffer ) ;
i++ ;
}
printf( ">--------------------------------------------------\n" ) ;
}
int main()
{
// Declare The Local Memory Variables
SQLHANDLE EnvHandle = 0;
SQLHANDLE ConHandle = 0;
SQLHANDLE StmtHandle = 0;
SQLRETURN RetCode = SQL_SUCCESS;
SQLCHAR SQLStmt[255];
SQLCHAR JobType[10];
SQLCHAR EmpNo[10];
SQLCHAR LastName[25];
SQLCHAR FirstName[15];
int EmpId=1002;
/**
* INITIALIZATION
**/
// Allocate An Environment Handle
SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE,&EnvHandle);
// Set The ODBC 应用程序 Version To 3.x
printf("Setting the ODBC version... \n");
if (EnvHandle != 0)
SQLSetEnvAttr(EnvHandle, SQL_ATTR_ODBC_VERSION,(SQLPOINTER) SQL_OV_ODBC3,
SQL_IS_UINTEGER);
// Allocate A Connection Handle
printf("Creating Connection handle... \n");
if (EnvHandle != 0)
SQLAllocHandle(SQL_HANDLE_DBC, EnvHandle,&ConHandle);
// Connect To The Appropriate Data Source
if (ConHandle != 0)
{
RetCode = SQLConnect(ConHandle, (SQLCHAR *) "SAMPLE",SQL_NTS,
(SQLCHAR *) "app",SQL_NTS,
(SQLCHAR *) "app", SQL_NTS);
printf("Got the connection... \n");
}
/*
* TRANSACTION PROCESSING
**/
// Allocate An SQL Statement Handle
if (ConHandle != 0 && RetCode == SQL_SUCCESS)
SQLAllocHandle(SQL_HANDLE_STMT, ConHandle,&StmtHandle);
else{
printf("Error getting connection:\n");
print_error((SQLSMALLINT)SQL_HANDLE_DBC,ConHandle,
RetCode,__LINE__,__FILE__ );
return(0);
}
// Define A SELECT SQL Statement That Uses A Parameter
strcpy((char *) SQLStmt, "INSERT INTO EMPLOYEE VALUES (?,?,?,?, \
CURRENT DATE)");
RetCode = SQLPrepare(StmtHandle, SQLStmt, SQL_NTS);
if(RetCode!=SQL_SUCCESS)
{
printf("Error preparing the insert statement:\n");
print_error((SQLSMALLINT)SQL_HANDLE_STMT,StmtHandle,
RetCode,__LINE__,__FILE__ );
}
RetCode = SQLBindParameter(StmtHandle, 1,SQL_PARAM_INPUT, SQL_C_LONG,
SQL_INTEGER,sizeof(EmpId),
0, &EmpId ,sizeof(EmpId), NULL);
if(RetCode!=SQL_SUCCESS)
{
printf("Error binding the first param in insert:\n");
print_error((SQLSMALLINT)SQL_HANDLE_STMT,StmtHandle,
RetCode,__LINE__,__FILE__ );
}
RetCode = SQLBindParameter(StmtHandle, 2,SQL_PARAM_INPUT, SQL_C_CHAR,
SQL_CHAR,sizeof(FirstName),
0, FirstName,sizeof(FirstName), NULL);
if(RetCode!=SQL_SUCCESS)
{
printf("Error binding the second param in insert:\n");
print_error((SQLSMALLINT)SQL_HANDLE_STMT,StmtHandle,
RetCode,__LINE__,__FILE__ );
}
RetCode = SQLBindParameter(StmtHandle, 3,SQL_PARAM_INPUT, SQL_C_CHAR,
SQL_CHAR,sizeof(LastName),
0, LastName,sizeof(LastName), NULL);
if(RetCode!=SQL_SUCCESS)
{
printf("Error binding the third param in insert:\n");
print_error((SQLSMALLINT)SQL_HANDLE_STMT,StmtHandle,
RetCode,__LINE__,__FILE__ );
}
RetCode = SQLBindParameter(StmtHandle, 4,SQL_PARAM_INPUT, SQL_C_CHAR,
SQL_CHAR,sizeof(JobType),
0, JobType,sizeof(JobType), NULL);
if(RetCode!=SQL_SUCCESS)
{
printf("Error binding the fourth param in insert:\n");
print_error((SQLSMALLINT)SQL_HANDLE_STMT,StmtHandle,
RetCode,__LINE__,__FILE__ );
}
strcpy((char *) FirstName, "Robert");
strcpy((char *) LastName, "Evans");
strcpy((char *) JobType, "ENGINEER");
RetCode = SQLExecute(StmtHandle);
if (RetCode == SQL_SUCCESS)
{
printf("Successfully executed the insert statement...\n");
}
else
{
printf("Error executing insert statement:\n");
print_error((SQLSMALLINT)SQL_HANDLE_STMT,StmtHandle,
RetCode,__LINE__,__FILE__ );
}
// A SELECT Statement
strcpy((char *) SQLStmt, "SELECT EMPNO, LASTNAME FROM ");
strcat((char *) SQLStmt, "EMPLOYEE WHERE JOBTYPE = ?");
// Prepare The SQL Statement
RetCode = SQLPrepare(StmtHandle, SQLStmt, SQL_NTS);
if(RetCode!=SQL_SUCCESS)
{
printf("Error preparing:\n");
print_error((SQLSMALLINT)SQL_HANDLE_STMT,StmtHandle,
RetCode,__LINE__,__FILE__ );
}
// Bind The Parameter Marker Used In The SQL Statement To
// An 应用程序变量
RetCode = SQLBindParameter(StmtHandle, 1,SQL_PARAM_INPUT, SQL_C_CHAR,
SQL_CHAR,sizeof(JobType),
0, JobType,sizeof(JobType), NULL);
if(RetCode!=SQL_SUCCESS)
{
printf("Error binding:\n");
print_error((SQLSMALLINT)SQL_HANDLE_STMT,StmtHandle,
RetCode,__LINE__,__FILE__ );
}
// Populate The "Bound" 应用程序变量
strcpy((char *) JobType, "ENGINEER");
// Execute The SQL Statement
RetCode = SQLExecute(StmtHandle);
// If The SQL Statement Executed Successfully, Retrieve
// The Results
if (RetCode == SQL_SUCCESS)
{
printf("Successfully executed the select statement...\n");
// Bind The Columns In The Result Data Set Returned
// To 应用程序变量s
SQLBindCol(StmtHandle, 1, SQL_C_CHAR, (SQLPOINTER)EmpNo,
sizeof(EmpNo), NULL);
SQLBindCol(StmtHandle, 2, SQL_C_CHAR, (SQLPOINTER)
LastName, sizeof(LastName), NULL);
// While There Are Records In The Result Data Set
// Produced, Retrieve And Display Them
while (RetCode != SQL_NO_DATA)
{
RetCode = SQLFetch(StmtHandle);
if (RetCode != SQL_NO_DATA)
printf("%-8s %s\n", EmpNo, LastName);
}
}else
{
printf("Error executing select statement:\n");
print_error((SQLSMALLINT)SQL_HANDLE_STMT,StmtHandle,
RetCode,__LINE__,__FILE__ );
}
// Commit The Transaction
RetCode = SQLEndTran(SQL_HANDLE_DBC, ConHandle,SQL_COMMIT);
/**
* TERMINATION
**/
// Free The SQL Statement Handle
if (StmtHandle != 0)
SQLFreeHandle(SQL_HANDLE_STMT, StmtHandle);
// Terminate The Data Source Connection
if (ConHandle != 0)
RetCode = SQLDisconnect(ConHandle);
// Free The Connection Handle
if (ConHandle != 0)
SQLFreeHandle(SQL_HANDLE_DBC, ConHandle);
// Free The Environment Handle
if (EnvHandle != 0)
SQLFreeHandle(SQL_HANDLE_ENV, EnvHandle);
// Return Control To The OS
return(0);
}
|
参考资料
关于作者  | |  | Rajesh Kartha 是 IBM Data Management 的 Cloudscape 小组的软件工程师。 |
对本文的评价
|  |