文章来自达梦技术社区http://bbs.dameng.com/
摘要:API是达梦数据库最低层的客户端接口,OCI则是ORACLE数据库最低层的客户端接口。前阵子我们根据客户需求按照ORACLE中OCI的标准开发出了一套达梦的OCI接口函数,为了方便大家了解和使用OCI,本文针对API和OCI这两种接口在连接、参数绑定、执行和结果集绑定等基本操作上进行对比,希望对大家有所帮助。
1. 架构
a) API
达梦的API架构采用的是ODBC标准中的架构,主要分为三层。顶层为环境句柄层,中层为连接句柄层,底层为语句句柄层。这三层的关系明确,下层隶属于上层,而每层之间又互不干扰。位于顶层的环境句柄是相互独立的,连接句柄从环境句柄上分配以后再建立连接,然后从连接上分配语句句柄用来执行SQL语句。一个环境句柄可以分配多个连接句柄,这些连接句柄相互独立,同样,从一个连接句柄上又可以分配多个语句句柄。下面用图来表示这三者的关系。
b) OCI
达梦OCI的架构则比较简单,它以环境句柄为主导,其它的一切句柄都从环境句柄上分配,而环境句柄上的上下文句柄和连接句柄只允许被分配一个实例。
2. 连接操作
a) API
达梦API在进行连接数据库操作时,主要可分为三步:第一,申请环境句柄;第二,在环境句柄上申请连接语句;第三,调用连接函数建立连接。请看下面示例:
dm_henv henv = NULL;//环境句柄
dm_hdbc hdbc = NULL;//连接句柄
dm_hstmt hstmt = NULL;//语句句柄
/*创建API运行环境*/
dm_api_init();
/*申请一个环境句柄*/
dm_alloc_env(&henv);
/*在环境句柄上面申请一个连接句柄*/
dm_alloc_connect(henv,&hdbc);
/*跟数据库接立连接*/
dm_login_port(hdbc, "localhost", "SYSDBA", "SYSDBA", 12345);
/*连接成功以后,才能在连接句柄上申请语句句柄*/
dm_alloc_stmt(hdbc,&hstmt);
/*在这里可以使用语句句柄执行SQL了*/
/*在处理完工作任务以后,可以做下面断开的操作*/
/*释放语句句柄*/
dm_free_stmt(hstmt);
/*断开与数据库之间的连接*/
dm_logout(hdbc);
/*释放连接句柄*/
TAPIc(hdbc, dm_free_connect(hdbc));
/*释放环境句柄*/
TAPIv(henv, dm_free_env(henv));
b) OCI
达梦OCI的连接跟API流程差不多,只是申请的句柄不一样。主要也可分为四步:第一,申请环境句柄;第二,在环境句柄上申请错误句柄;第三,在环境句柄上申请上下文句柄;第四,调用函数建立连接。请看下面示例:
#include "oci.h"
#include
#include
#include
OCIEnv *envhp; //环境句柄
OCIError *errhp; //错误句柄
OCISvcCtx *svchp; //上下文句柄
OCIStmt *stmthp = (OCIStmt *) NULL;//语句句柄
sword init_handles(OCIEnv **envhp, OCISvcCtx **svchp, OCIError **errhp);
sword finish_demo(boolean loggedon, OCIEnv *envhp, OCISvcCtx *svchp, OCIError *errhp, OCIStmt *stmthp, text *userid);
sword log_on(OCIEnv *envhp, OCIError *errhp, OCISvcCtx *svchp, text *uid, text *pwd, text *cstring);
void report_error(OCIError *errhp);
int main()
{
text *cstring =(text*)"127.0.0.1";//注意,如果你配置了dm_oci.conf文件(如:MyServer = (127.0.0.1)),那么你也可以在这里指定一个服务名MyServer
text *username = (text *)"SYSDBA";
text *password = (text *)"SYSDBA";
int logged_on = FALSE;
//初始化各种句柄
if (init_handles(&envhp, &svchp, &errhp))
{
(void) printf("FAILED: init_handles()\n");
return finish_demo(logged_on, envhp, svchp, errhp, stmthp, username);
}
//登录到数据库服务
if (log_on(envhp, errhp, svchp, username, password, cstring))
{
(void) printf("FAILED: log_on()\n");
return finish_demo(logged_on, envhp, svchp, errhp, stmthp, username);
}
logged_on = TRUE;
//
//在这里做SQL动作
//
//登出数据库服务,并清理各种句柄
return finish_demo(logged_on, envhp, svchp, errhp, stmthp, username);
}
sword init_handles(OCIEnv **envhp, OCISvcCtx **svchp, OCIError **errhp)
{
(void) printf("Environment setup ....\n");
/* 初始化OCI应用环境 */
if (OCIInitialize(OCI_DEFAULT, (dvoid *)0,
(dvoid * (*)(dvoid *, size_t)) 0,
(dvoid * (*)(dvoid *, dvoid *, size_t))0,
(void (*)(dvoid *, dvoid *)) 0 ))
{
(void) printf("FAILED: OCIInitialize()\n");
return OCI_ERROR;
}
/* 初始化环境句柄 */
if (OCIEnvInit((OCIEnv **) envhp, (ub4) OCI_DEFAULT,
(size_t) 0, (dvoid **) 0 ))
{
(void) printf("FAILED: OCIEnvInit()\n");
return OCI_ERROR;
}
/* 从环境句柄上分配一个上下文句柄 */
if (OCIHandleAlloc((dvoid *) *envhp, (dvoid **) svchp,
(ub4) OCI_HTYPE_SVCCTX, (size_t) 0, (dvoid **) 0))
{
(void) printf("FAILED: OCIHandleAlloc() on svchp\n");
return OCI_ERROR;
}
/* 从环境句柄上分配一个错误信息句柄 */
if (OCIHandleAlloc((dvoid *) *envhp, (dvoid **) errhp,
(ub4) OCI_HTYPE_ERROR, (size_t) 0, (dvoid **) 0))
{
(void) printf("FAILED: OCIHandleAlloc() on errhp\n");
return OCI_ERROR;
}
return OCI_SUCCESS;
}
/* ----------------------------------------------------------------- */
/* 使用给定的用户名和口令登录到指定的数据库服务上 */
/* ----------------------------------------------------------------- */
sword log_on(OCIEnv *envhp, OCIError *errhp, OCISvcCtx *svchp, text *uid, text *pwd, text *cstring)
{
(void) printf("Logging on as %s ....\n", uid);
/* Set the authentication handle in the Service handle */
if (OCILogon(envhp, errhp, &svchp, uid, strlen((char*)uid),
pwd, strlen((char*)pwd), cstring, strlen((char*)cstring)))
{
(void) printf("FAILED: OCILogon()\n");
return OCI_ERROR;
}
(void) printf("%s logged on.\n", uid);
return OCI_SUCCESS;
}
/* ----------------------------------------------------------------- */
/* 释放申请的各种句柄 */
/* ----------------------------------------------------------------- */
void free_handles(OCIEnv *envhp, OCISvcCtx *svchp,OCIError *errhp, OCIStmt *stmthp)
{
(void) printf("Freeing handles ...\n");
if (svchp)
(void) OCIHandleFree((dvoid *) svchp, (ub4) OCI_HTYPE_SVCCTX);
if (errhp)
(void) OCIHandleFree((dvoid *) errhp, (ub4) OCI_HTYPE_ERROR);
if (stmthp)
(void) OCIHandleFree((dvoid *) stmthp, (ub4) OCI_HTYPE_STMT);
if (envhp)
(void) OCIHandleFree((dvoid *) envhp, (ub4) OCI_HTYPE_ENV);
return;
}
/*---------------------------------------------------------------------*/
/* 断开连接,并释放各种句柄 */
/*---------------------------------------------------------------------*/
sword finish_demo(boolean loggedon, OCIEnv *envhp, OCISvcCtx *svchp, OCIError *errhp, OCIStmt *stmthp, text *userid)
{
if (loggedon)
OCILogoff(svchp, errhp);
free_handles(envhp, svchp, errhp, stmthp);
return OCI_SUCCESS;
}
void report_error(OCIError *errhp)
{
text msgbuf[512];
sb4 errcode = 0;
(void) OCIErrorGet((dvoid *) errhp, (ub4) 1, (text *) NULL, &errcode,
msgbuf, (ub4) sizeof(msgbuf), (ub4) OCI_HTYPE_ERROR);
(void) printf("ERROR CODE = %d", errcode);
(void) printf("%.*s\n", 512, msgbuf);
return;
}
3. 参数绑定
a) API
在连接建立以后,申请完语句句柄就可以执行SQL语句了,但是如果你的语句中带有参数,那么你可能还需要对参数进行绑定后才能执行。在API中,SQL中的参数是以?号形式表现的,在绑定时需要调用dm_bind_paramr函数接口来实现。dm_bind_param函数绑定的时候是按给定的参数位置来跟语句中的参数建立对应关系的,该函数第二个参数便是参数的位置,也就是对应?号所在SQL语句中的位置。下面给出该函数中每个参数的注解:
dm_bool
dm_bind_param(
dm_hstmt dmstmt, /* in: 用来执行的语句句柄*/
unsigned short paramid, /* in: 参数的位置*/
unsigned short iotype, /* in: 参数的类型(输入/输出/输入输出)*/
long sql_c_type, /* in: 参数的C数据类型(缓冲区中存的值的类型)*/
unsigned long type, /* in: 参数对应数据库中达梦的数据类型*/
dm_uint3264 col_size, /* in: 列的大小,也可以说是精度*/
unsigned short dec_digit, /* in: 列的刻度*/
void* value, /* in: 缓冲区指针*/
dm_int3264 len, /* in: 缓冲区中单个值的长度, 这个参数对于固定数据类型来说是被忽略的*/
dm_int3264* len_or_ind, /* in: 要写入单个值的长度*/
unsigned short ptr_len /* in: len_or_ind 参数的大小*/
);
下面我们列举一个带参数的SQL语句如何绑定:
int c1;
char c2[100];
dm_int3264 len1, len2;
//绑定第一个问号参数
dm_bind_param(dmstmt, 1, DM_PARAM_INPUT, TYPE_INTEGER, DM_DATA_INT, 10, 0, &c1, 0, &len1, DM_DESC_PTR_LEN_DEFAULT);
//绑定第二个问号参数
dm_bind_param(dmstmt, 2, DM_PARAM_INPUT, TYPE_CHAR, DM_DATA_CHAR, 100, 0, c2, 100, &len2, DM_DESC_PTR_LEN_DEFAULT);
//下面对要插入的参数赋值
c1 = 1;
len1 = 4;//值c1的大小,按字节计算
memset(c2, ‘a’, 100);
len2 = 100;//参数c2的大小,按字节计算
//执行带参数的语句,执行完成以后1和’aaa..’就会被插入到表t的c1和c2字段中
dm_direct_exec(dmstmt, “insert into t(c1, c2) values(?, ?)”);
b) OCI
OCI的参数绑定动作需要使用OCIBindByPos或OCIBindByName函数协同OCIBindArrayOfStruct函数来完成,并且绑定动作必需放在准备语句和执行语句之间进行。在OCI中,为了兼容ORACLE的语法,SQL语句中的参数是以冒号开头的,冒号后面跟一个名字用来表示参数的名称,所以,OCI绑定参数时即可以按参数位置绑定(OCIBindByPos),也可以按参数名来绑定(OCIBindByName),OCIBindArrayOfStruct函数是用来指定在多行参数绑定时,每个参数跟下一个参数的偏移量。具体有解释可以参考OCI手册。下面列举OCIBindByPos的参数:
sword OCIBindByName (OCIStmt *stmtp, /*语句句柄*/
OCIBind **bindp, /*输出的绑定结构句柄*/
OCIError *errhp,/*错误信息句柄*/
CONST OraText *placeholder, /*参数的名称*/
sb4 placeh_len, /*参数名称的长度*/
dvoid *valuep, /*参数值的缓冲区指针*/
sb4 value_sz, /*单个值的长度*/
ub2 dty, /*参数的OCI数据类型*/
dvoid *indp, /*参数值的指示符*/
ub2 *alenp, /*下面都是保留参数*/
ub2 *rcodep,
ub4 maxarr_len,
ub4 *curelep,
ub4 mode)
对应上面API的实现,OCI的代码应该如下:
int c1;
char c2[100];
text *sqlstmt = (text*)" insert into t(c1, c2) values(?, ?)";
//在绑定前先要准备语句
OCIStmtPrepare(stmthp, errhp, sqlstmt, (ub4)strlen((char *)sqlstmt),
(ub4) OCI_NTV_SYNTAX, (ub4) OCI_DEFAULT);
//绑定:c1参数
OCIBindByName (stmtp, NULL, errhp, “:c1”, 3, &c1, 4, SQLT_INT, NULL, NULL, NULL, 0, NULL, 0);
//绑定:c2参数
OCIBindByName (stmtp, NULL, errhp, “:c2”, 3, &c2, 100, SQLT_AFC, NULL, NULL, NULL, 0, NULL, 0);
//下面对要插入的参数赋值
c1 = 1;
len1 = 4;//值c1的大小,按字节计算
memset(c2, ‘a’, 100);
len2 = 100;//参数c2的大小,按字节计算
//执行带参数的语句,执行完成以后1和’aaa..’就会被插入到表t的c1和c2字段中
OCIStmtExecute(svchp, stmthp, errhp, (ub4)1, (ub4) 0, (CONST OCISnapshot*) 0, (OCISnapshot*) 0, (ub4) OCI_DEFAULT);
4. 执行
a) API
API在执行一条语句时,有两种方式:一种是准备后执行;另一种是直接执行。在准备执行时要先调用dm_prepare来准备,随后再调用dm_exec执行准备好的语句。直接执行是调用dm_direct_exec函数,直接用语句作为参数传入该函数里面。
b) OCI
OCI的执行只有一种方式,必需先调用OCIStmtPrepare来准备语句,再调用OCIStmtExecute来执行准备好的语句,当然,OCI和API在执行准备好的语句时,都可以一次准备,多次执行。
5. 结果集绑定
a) API
绑定结果集是客户端的重要功能,在API里面,支持列绑定和行绑定两种方式,并且允许一次性返回多行,但是绑定函数只有一个dm_bind_column函数,它在列绑定时,value参数是实际的缓冲区指针;而在行绑定的情况下给的则是一个偏移值,真正的缓冲区需要通过描述符来设置。行绑定要比列绑定复杂得多,可以参看ODBC的行绑资料来了解详情。在要求一次获取返回多行时,还需要设置语句句柄的DM_ATTR_ROW_ARRAY_SIZE属性值来达到这个目的。dm_bind_column函数的注释如下:
dm_bool
dm_bind_column(
dm_hstmt dmstmt, /*语句句柄*/
unsigned short colid, /*结果集列的位置,从1开始*/
unsigned int type, /*列的达梦服务器数据类型*/
int sql_c_type, /*列的C数据类型*/
void* value, /*数据缓冲区指针*/
dm_int3264 len, /*单个值的最大长度*/
dm_int3264* outlen, /*实际的返回长度*/
unsigned short ptr_len/* len_or_ind指针的大小*/
);
下面举一个简单的列绑定例子,该例子中结果集有两列,第一列为整数,第二列为字符串。
int c1;
int len1;
int c2[101];
int len2;
c2[100] = 0;
//执行查询语句生成结果集
dm_direct_exec(hstmt, “select c1, c2 from t”);
//绑定第一列
dm_bind_column(hstmt, 1, DM_DATA_INT, TYPE_INT, &c1, 4, &len1, 0);
//绑定第二列
dm_bind_column(hstmt, 2, DM_DATA_CHAR, TYPE_CHAR, c2, 100, &len2, 0);
//提取结果集
while (dm_fetch(hstmt, NULL) == 0) {
printf("c1:%d, c2:%s", c1, c2);
}
//提取完以后需要关闭游标句柄
dm_close_stmt(hstmt);
b) OCI
OCI在绑定的时候只有一种形式,通过调用OCIDefineByPos来实现对每个列的绑定,如果你需要一次性获取多行,你还需要调用OCIDefineArrayOfStruct函数来约定每个值跟下一个值之间的偏移量。一次性获取的行数可以在OCIStmtExecute或OCIStmtFetch函数中指定,下面列出对OCIDefineByPos的注释:
sword OCIDefineByPos (
OCIStmt *stmtp, /*语句句柄*/
OCIDefine **defnp, /*绑定输出指针*/
OCIError *errhp,/*错误句柄*/
ub4 position, /*绑定列的位置*/
dvoid *valuep, /*缓冲区指针*/
sb4 value_sz, /*单个值的大小*/
ub2 dty,/*要转化成的OCI数据类型*/
dvoid *indp, /*空值指示符*/
ub2 *rlenp, /*实际返回长度指示符*/
ub2 *rcodep, /*下面两参数为保留参数*/
ub4 mode
)
跟据上面API的例子,在这里给出OCI的代码:
int c1;
int len1;
int c2[101];
int len2;
c2[100] = 0;
text *sqlstmt = (text*)"select c1, c2 from t ";
//在绑定前先要准备语句
OCIStmtPrepare(stmthp, errhp, sqlstmt, (ub4)strlen((char *)sqlstmt),
(ub4) OCI_NTV_SYNTAX, (ub4) OCI_DEFAULT);
//绑定第一列
OCIDefineByPos(stmthp, NULL, errhp, (ub4) 1,
(dvoid *) &c1, (sb4) sizeof(int), (ub2) SQLT_INT,
(dvoid *) 0, (ub2 *) 0, (ub2 *) 0, (ub4) OCI_DEFAULT)
//绑定第二列
OCIDefineByPos(stmthp, NULL, errhp, (ub4) 2,
(dvoid *) c2, (sb4) 100, (ub2) SQLT_AFC,
(dvoid *) 0, (ub2 *) 0, (ub2 *) 0, (ub4) OCI_DEFAULT)
OCIStmtExecute(svchp, stmthp, errhp, (ub4)0, (ub4) 0, (CONST OCISnapshot*) 0, (OCISnapshot*) 0, (ub4) OCI_DEFAULT);
//在这里每次提取一行
while (OCIStmtFetch (stmthp, errhp, 1, OCI_FETCH_NEXT, 0) == 0) {
printf("c1:%d, c2:%s", c1, c2);
}
6. 释放连接
a) API
在执行完所有的SQL操作以后,需要断开跟数据库服务器的连接。API在断开时需要释放所申请的所有句柄,包括语句句柄,连接句柄和环境句柄,语句句柄需要在断开连接之前释放。
dm_close_stmt(hstmt);
/*释放语句句柄*/
dm_free_stmt(hstmt);
/*断开与数据源之间的连接*/
dm_logout(hdbc);
/*释放连接句柄*/
dm_free_connect(hdbc);
/*释放环境句柄*/
dm_free_env(henv);
b) OCI
OCI跟API一样,断开连接以后需要释放所有的句柄,这些释放工作都是通过调用OCIHandleFree来完成,例子可以参看连接部分的代码。
7. 结束语
我们可以看到,API和OCI在使用上都有几份类似,在连接和释放上面,两者区别不大,但是在参数和结果集绑定上面,API使用的时候比较的灵活多变,有很多参数可以灵活的指定,但是使用起来感觉比较复杂,而OCI则简单多了,但是同样可以实现API绑定的大部分功能。如果想要更详细的了解API和OCI,那么请参看ODBC标准手册和ORACLE的OCI说明手册,哪上面有更加详细的使用介绍。
来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/23392679/viewspace-627971/,如需转载,请注明出处,否则将追究法律责任。
转载于:http://blog.itpub.net/23392679/viewspace-627971/