参考文档《Oracle Call Interface Programmer's Guide》
在Orcale和DM数据库提供的API中,通过OCI方式接口连接数据库的方法有多个,这里只讨论OCILogon和OCIServerAttach的比较。
1、官方描述
根据文档里的描述:
OCILogon():This function is used to create a simple logon session for an application.
OCILogon()适用于应用程序对于某一数据库连接仅支持单用户进程。
OCI内部过程的调用顺序基本如下:
OCIEnvCreate
OCIHandleAlloc <ERROR>
OCILogon
OCILogoff
OCIHandleFree <ERROR>
OCIHandleFree <ENV>
OCIServerAttach():This call is used to create an association between an OCI application and a particular server
OCIServerAttach()适用于应用程序对单一数据库连接需要维护多个用户会话
每个句柄在这里都是手工去创建。所有的OCI句柄都有其相关属性,这些属性存储了一些有用的数据。可以使用OCIAttrGet()获取对应信息,也可以通过OCIAttrSet()进行修改。OCIServerAttach() 创建了一个OCI操作的数据服务访问路径,OCISessionBegin() 确立用户会话连接。这里完成后,才可以进行实际的数据操作。注意这里的OCIAttrSet()和OCIServerAttach()的调用顺序不是固定的,类似的 OCISessionBegin() 和 OCIAttrSet() 先后顺序也是可以互换的。
2、结论
单个用户,单个连接
单个用户单个连接这种是最简单的登录方式,可以在应用对每个数据库连接仅维护一个用户会话的情况下使用
当应用调用OCILogon2()或OCILogon()时,OCI库对传递给它的服务上下文句柄进行初始化,为发请求的用户创建到指定Oracle数据库的连接。
非代理的多会话或多连接
非代理多会话或连接方式,通过显式地添加和调用启用会话函数来维护一个数据库连接上的多个用户会话和连接。 附加到 Oracle 数据库和开始会话的特定函数有:
OCIServerAttach()——为OCI操作创建到Oracle数据库地访问路径
OCISessionBegin()——为用户创建到特定Oracle数据库的会话。这个函数对于需要在Oracle数据库上执行操作的用户来说是必须的。
再次调用OCISessionBegin()时使用不同的服务上下文句柄和会话上下文句柄会导致前一个用户登出,并引发错误。要想同时运行两个不可互相替代的会话,第二次调用OCISessionBegin()函数时必须指定一个相同的服务上下文句柄和一个新的会话上下文句柄。
3、拓展介绍-句柄
3.1 定义
几乎每个OCI调用的参数列表中都会包含一个或多个句柄。一个句柄就是一个指向由OCI库分配的存储空间的不透明指针。应用可以使用句柄存储上下文或连接信息,例如环境或服务上下文句柄,也可以存储OCI函数或数据的信息,例如错误或describe句柄。句柄让编程变得简单,因为是使用库来维护数据而非应用来维护数据。
大多数OCI应用必须要获取存储在句柄中的信息,所以设计通过set和get属性的OCI调用,OCIAttrGet()和OCIAttrSet()来访问或设置这些信息。
3.2 句柄类型
下表列出了OCI中的各类句柄,每种句柄类型对应的C数据类型以及在OCI调用中标识句柄类型的常量表示
描述 | C数据类型 | 类型常量 |
OCI环境句柄 | OCIEnv | OCI_HTYPE_ENV |
OCI错误句柄 | OCIError | OCI_HTYPE_ERROR |
OCI服务和上下文句柄 | OCISvcCtx | OCI_HTYPE_SVCCTX |
OCI语句句柄 | OCIStmt | OCI_HTYPE_STMT |
OCI绑定句柄 | OCIBind | OCI_HTYPE_BIND |
OCI定义句柄 | OCIDefine | OCI_HTYPE_DEFINE |
OCI描述句柄 | OCIDescribe | OCI_HTYPE_DESCRIBE |
OCI服务器句柄 | OCIServer | OCI_HTYPE_SERVER |
OCI用户会话句柄 | OCISesseion | OCI_HTYPE_SESSION |
OCI验证信息句柄 | OCIAuthInfo | OCI_HTYPE_AUTHINFO |
OCI连接池句柄 | OCICPool | OCI_HTYPE_CPOOL |
OCI会话池句柄 | OCISPool | OCI_HTYPE_SPOOL |
OCI事务句柄 | OCITrans | OCI_HTYPE_TRANS |
OCI复杂对象检索(COR)句柄 | OCIComplexObject | OCI_HTYPE_COMPLEXOBJECT |
OCI线程句柄 | OCIThreadHandle | 不适用 |
OCI订阅句柄 | OCISubscription | OCI_HTYPE_SUBSCRIPTION |
OCI直接路径上下文句柄 | OCIDirPathCtx | OCI_HTYPE_DIRPATH_CTX |
OCI直接路径函数上下文句柄 | OCIDirPathFuncCtx | OCI_HTYPE_DIRPATH_FN_CTX |
OCI直接路径列数组句柄 | OCIDirPathColArray | OCI_HTYPE_DIRPATH_COLUMN_ARRAY |
OCI直接路径流句柄 | OCIDirPathStream | OCI_HTYPE_DIRPATH_STREAM |
OCI进程句柄 | OCIProcess | OCI_HTYPE_PROC |
OCI管理句柄 | OCIAdmin | OCI_HTYPE_ADMIN |
OCI HA 事件句柄 | OCIEvent | 不适用 |
3.3 分配和释放句柄
除了绑定、定义、线程句柄之外,其他句柄的分配都是针对一个特定环境句柄来说的。在执行分配句柄的调用时,需要将环境句柄作为参数之一传入,因此分配的句柄是特定于一个环境的。
绑定和定义句柄是特定于语句句柄分配的,其中包含语句句柄所代表的语句的一些信息。
绑定和定义局部由OCI库隐式地分配,不需要用户分配。
环境句柄由OCIEnvCreate()或OCIEnvNlsCreate()的调用完成分配和初始化。任何一个OCI应用都需要用到这两个函数中的一个。
所有用户分配的句柄的初始化都通过调用OCI句柄分配函数OCIHandleAlloc()来完成。
句柄的类型包括:会话池句柄,直接路径上下文句柄,线程句柄,COR句柄,订阅句柄,描述句柄,语句句柄,服务上下文句柄,错误句柄,服务器句柄,连接池句柄,HA事件句柄,管理句柄。
线程句柄通过OCIThreadHndInit()调用进行分配。
所有应用必须释放不再使用的句柄,释放句柄的通过OCIHandleFree()完成。
注意:
当一个母句柄释放,所有与其关联的子句柄都将一并释放并且不能再被使用。例如一个语句句柄被释放后,任何与其相关的绑定句柄和定义句柄都会一并释放。
句柄减少了对全局变量的需求,同时简化了错误报告,可以使用一个错误句柄来返回错误和诊断信息。
4、OCIServerAttach()连接方式demo
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <malloc.h>
#include "DCI.h"
/* 声明句柄 */
OCIEnv *envhp; /* 环境句柄 */
OCISvcCtx *svchp; /* 服务环境句柄 */
OCIServer *srvhp; /* 服务器句柄 */
OCISession *authp; /* 会话句柄 */
OCIStmt *stmthp; /* 语句句柄 */
OCIDescribe *dschp; /* 描述句柄 */
OCIError *errhp; /* 错误句柄 */
OCIDefine *defhp[3]; /* 定义句柄 */
OCIBind *bidhp [4]; /* 绑定句柄 */
sb2 ind[3]; /* 指示符变量 */
/* 绑定select结果集的参数 */
text szpersonid[11]; /* 存储personid列 */
text szsex[2]; /* 存储sex列 */
text szname[51]; /* 存储name列 */
text szemail[51]; /* 存储mail列 */
text szphone[26]; /* 存储phone列 */
char sql[256]; /* 存储执行的sql语句*/
int main(int argc, char *argv[])
{
int errcno;
char errbuf[512]= "";
sb4 errcode;
char strServerName[50];
char strUserName[50];
char strPassword[50];
/* 设置服务器,用户名和密码 */
strcpy(strServerName,"localhost");
strcpy(strUserName,"SYSDBA");
strcpy(strPassword,"SYSDBA");
/* 初始化OCI应用环境*/
OCIInitialize(OCI_DEFAULT, NULL, NULL, NULL, NULL);
/* 初始化环境句柄 */
OCIEnvInit(&envhp, OCI_DEFAULT,0, 0);
/* 分配句柄 */
OCIHandleAlloc(envhp, (dvoid**)&svchp, OCI_HTYPE_SVCCTX, 0, 0); /* 服务器环境句柄 */
OCIHandleAlloc(envhp, (dvoid**)&srvhp, OCI_HTYPE_SERVER, 0, 0); /* 服务器句柄 */
OCIHandleAlloc(envhp, (dvoid**)&authp, OCI_HTYPE_SESSION, 0, 0); /* 会话句柄 */
OCIHandleAlloc(envhp, (dvoid**)&errhp, OCI_HTYPE_ERROR, 0, 0); /* 错误句柄 */
OCIHandleAlloc(envhp, (dvoid**)&dschp, OCI_HTYPE_DESCRIBE,0,0); /* 描述符句柄 */
/* 连接服务器 */
OCIServerAttach(srvhp, errhp,(text *)strServerName, (sb4)strlen(strServerName),OCI_DEFAULT);
/* 设置用户名和密码 */
OCIAttrSet(authp,OCI_HTYPE_SESSION,(text *)strUserName,
(ub4)strlen(strUserName),OCI_ATTR_USERNAME,errhp);
OCIAttrSet(authp,OCI_HTYPE_SESSION,(text *)strPassword,
(ub4)strlen(strPassword), OCI_ATTR_PASSWORD,errhp);
/* 设置服务器环境句柄属性 */
OCIAttrSet ((dvoid*)svchp, (ub4) OCI_HTYPE_SVCCTX,
(dvoid*)srvhp, (ub4) 0, OCI_ATTR_SERVER, errhp);
OCIAttrSet(svchp, OCI_HTYPE_SVCCTX,(dvoid*)authp,
0, OCI_ATTR_SESSION, errhp);
/* 创建并开始一个用户会话 */
OCISessionBegin (svchp, errhp, authp,OCI_CRED_RDBMS,OCI_DEFAULT);
OCIHandleAlloc(envhp, (dvoid**)&stmthp,OCI_HTYPE_STMT, 0, 0); /* 语句句柄 */
/************************************************************************/
//结束会话
OCISessionEnd(svchp, errhp, authp, (ub4) 0);
//断开与数据库的连接
OCIServerDetach(srvhp, errhp, OCI_DEFAULT);
//释放OCI句柄
OCIHandleFree((dvoid*)dschp, OCI_HTYPE_DESCRIBE);
OCIHandleFree((dvoid*)stmthp, OCI_HTYPE_STMT );
OCIHandleFree((dvoid*)errhp, OCI_HTYPE_ERROR);
OCIHandleFree((dvoid*)authp, OCI_HTYPE_SESSION );
OCIHandleFree((dvoid*)svchp, OCI_HTYPE_SVCCTX);
OCIHandleFree((dvoid*)srvhp, OCI_HTYPE_SERVER);
return 0;
}
参考blog:https://www.cnblogs.com/Winnie-Z/articles/16264822.html