OCI

OCI
2011年12月05日
  [b]OCI编程的一般过程[/b]
  与OCI7.3中使用的宿主语言定义变量存储空间(很拗口,不用管它)的方式不同,OCI9以后已不再使用原有的变量结构来初始化及维护数据库的信息,而改用句柄的形式来和Oracle数据库进行交互。将常用的句柄定义在一个结构内,方便程序维护:
  ?
  1
  2
  3
  4
  5
  6
  7
  8
  9
  typedef struct _OCI_HANDLE
  {
  OCIEnv *phEnv; //环境句柄,要使用oracle数据库,必须首先获得环境句柄
  OCISvcCtx *phService; //oracle的服务句柄,也可以说是连接句柄。
  OCIError *phErr; //oracle的错误句柄,可以获取错误信息
  OCIStmt *phStmt; //oracle的语句描述句柄
  OCIServer *phServer; //Oracle的服务器句柄
  OCISession *phSession; //Oracle会话句柄
  }OCIHANDLE, *LPOCIHANDLE;
  OCI9编程的一般步骤有:初始化环境句柄、生成其他各类句柄、建立数据库连接进行登录、执行SQL语句,对返回的结果进行处理、终止用户会话,断开连接,释放各种句柄。
  
  
   上图给出OCI初始化的一个过程,OCI能初始化成功的前提当然是系统中已经安装或设置了Oracle的client端(在本文第四节有介绍)。其中步骤(3)到(7)都分别调用OCIHandleAlloc()函数进行分配,顺序可以不同,它们都只依赖环境句柄;步骤(1)和(2)可以使用OCIEnvCreate()函数替换掉,这两种的初始化OCI环境的方法在不同的使用条件下是不同的,一般建议使用OCIEnvCreate()代替OCIInitialize()和OCIEnvInit(),因为OCIInitialize()和OCIEnvInit()主要是为了backwards-compatible。而如果是编写DLL更是应该使用OCIEnvCreate()函数,user‘guide是这样说的:
  If you are writing a DLL or a shared library using OCI library then this call should definitely be used instead of OCIInitialize() and OCIEnvInit() call.
  OCI各句柄初始化完毕后,接下来就是连接数据库,如下图:
  
  
  
  数据库连接好后可以执行SQL语句:一条SQL语句在OCI应用程序中的执行步骤一般如下:(1)准备SQL语句。(2)在SQL语句中绑定需要输入到SQL语句中的变量。(3)执行SQL语句。(4)获取SQL中的输出描述。(5)定义输出变量。(6)获取数据。具体过程及过程中调用的函数如下图所示。对于SQL中的定义语句(如CREATE,DROP)和控制语句(如GRANT,REVOKE),由于没有数据的输入输出,只需要图2中第一步和第三步即可。操作语句(如INSERT,DELETE,UPDATE)则需要执行前三步。而查询语句(如SELECT)不仅可能有数据输入,而且也有数据的输出,因此需要执行六个步骤。
  
  
   OCI编程的一般过程还是很清晰的,流程图都是一条线………
  [b]三、单次查询返回多行结果的实现[/b]
  设计的时候老大要求要像原有接口库那样一次查询返回多行,然后再在本地进行处理,以减少对数据库的访问。这本来是一个很正常的要求,但后面看了好些开源的OCI封装,发现它们的demo里都没有给出如何fetch多行……… 比如写的比较好的ocilib,demo中就没有给出(至少是以前的版本没给出,现在就不知道了),在写完这个OCI接口库大概半年后,再看ocilib的代码OCIDefineByPos()函数时,发现倒数第三第四个参数都是指针,说明可以fetch多行。进而发现ocilib可以通过OCI_SetFetchSize()函数来设置查询返回的行数。但为什么不在demo里给一个示例呢?就连该版本的文档里也没有这个函数的说明。很奇怪!没办法还是自己动手丰衣足食,使劲啃user’guide。
  需要fetch多行,首先要考虑执行select语句后,接收到数据放在什么地方?当然是放在缓冲区里了,在OCI里通过不同的变量函数绑定来告诉oracle client把从数据库取到的数据存放在什么地方。这里使用OCIDefineByPos()函数。下面以每次取100行为例给出具体步骤:
  1. 分配足够大的缓冲区m_pData = new unsigned char[m_DataLen * 100]。m_DataLen表示数据库表中每一行的长度(各个列长度之和),这样需要使用unsigned作Buffer,因为如果使用有符号char则取带时间的表会有问题。
  2. 根据各列的长度来定义各列在缓冲区中的位置:
  ?
  1
  2
  3
  4
  5
  6
  7
  8
  9
  10
  OCIDefineByPos(m_hOCI.phStmt,        //语句句柄
  &(m_vecColInfo.phdefine),//定义句柄
  m_hOCI.phErr,   
  i+1,              //列序号
  (ub1 *)(&(m_pData[pos])), //各列的位置pos等于当前列之前各列长度和乘以100
  m_vecColInfo.collen,   //对应列的长度 SQLT_STR,
  (m_vecColInfo.indp),   //指示器,因为每次最多要取100行,所以indp应设为维数为100的数组。
  (m_vecColInfo.rlenp),  //返回数据的真实长度,这里也应把rlenp设为维数100的数组
  0,
  OCI_DEFAULT));
  m_vecColInfo为保存各列信息的vector。值得注意的是OCIDefineByPos()的第八和第九个参数:第八个参数是指示器参数,在OCIStmtFectch后只是所取的对应数据是否完整(0表示完整),由于要取100行,则在m_vecColInfo中每一个列元素对应的结构中都应定义indp[100]的数组。第九个参数用于返回所取数据的实际长度,因此也需要在一个列元素的结构体中定义rlenp[100]的数组。还有一个需要注意的是第四个参数里的pos,pos用于指定该列保存在Buffer中的起始位置。如下表是数据库中某表,执行select查询该表前100行后,数据在缓冲区m_pData中保存数据的形式如图4:
  CarKey
  MakeKey
  ModelKey
  ColorKey
  Year
  1
  1
  1
  2
  2003
  2
  2
  1
  3
  2005
  3
  2
  1
  2
  2005
  …….
  …….
  ……..
  ……..
  ………
  100
  2
  1
  1
  2006
  
  
   上图可以看到OCI在fetch多行时,先将第一列的100行数据放入m_pData中,然后以列为单位每次取100行放入m_pData。因此pos变量的赋值应写为:pos += 100 * (m_vecColInfo[i-1].collen); 其中collen代表该列的长度。
  3. 获取数据:
  ?
  1
  2
  3
  4
  5
  OCIStmtFetch(m_hOCI.phStmt,
    m_hOCI.phErr,
    100,//每次取100行的数据
    OCI_FETCH_NEXT,
    OCI_DEFAULT);
  第三个参数设置为100后,执行OCIStmtFetch完毕后数据就填充到缓冲区中。这里需要注意的是最后一个fetch,因为最后一次fetch时数据库表中往往已经不足100行,所以每次执行OCIStmtFetch()函数完毕要需要检查其返回值,当返回值为OCI_NO_DATA时使用:
  ?
  1
  2
  3
  4
  5
  6
  OCIAttrGet(m_hOCI.phStmt,
  OCI_HTYPE_STMT,
  (dvoid *) &row_fetched,
  (ub4 *) NULL,
  (ub4) OCI_ATTR_ROWS_FETCHED,
  m_hOCI.phErr);
  row_fetched将返回剩下的行数,倒数第二个参数为OCI_ATTR_ROWS_FETCHED,在oci.h中是这样定义的:
  #define OCI_ATTR_ROWS_FETCHED 197 /* rows fetched in last call */
  在oci10中这里没有任何问题,但在一些较早的oci9版本中找不到OCI_ATTR_ROWS_FETCHED的定义……… 也就是说无法fetch多行?!迫不得已只能用oci10。可能ocilib在demo中没有fetch多行的示例也是出于这个考虑吧。
  [b]四、Oracle即时客户端(instantclient)的配置[/b]
  过去使用OCI需要安装oracle的客户端,Oracle的普通客户端一般都很庞大,Windows平台下的客户端就有700M。Oracle公司在10g版本后推出了大小只有30M的InstantClient(即时客户端)作为oracle的访问客户端。不需要安装就可以访问Oracle的服务器。
  [b]Windows平台下instantclient的配置和使用:[/b]
  下面以C:\Oracle为例介绍具体的配置过程。
  1.将instantclient的basic包及sqlplus包中所有文件解压至C:\Oracle。
  2.配置系统的环境变量:
  • 将 C:\Oracle 添加到 PATH 中(位于其他 Oracle 目录之前)。例如,在 Windows 2000 上,依次单击“开始”->“设置”->“控制面板”->“系统”->“高级”->“环境变量”,编辑系统变量列表中的 PATH。WindowXP上,右击“我的电脑”->“高级”->“环境变量”。
  • 添加用户环境变量 TNS_ADMIN 设置为C:\Oracle。
  • 设置必要的 Oracle 全球化语言环境变量, 添加用户环境变量NLS_LANG 中文对应的字符集是 SIMPLIFIED CHINESE_CHINA.ZHS16GBK
  3. 一共设置以下三个环境变量(以解压缩目录C:\Oracle为例)环境变量名 变量值
  path C:\Oracle
  TNS_ADMIN C:\Oracle
  ORACLE_HOME C:\Oracle (可选)
  NLS_LANG SIMPLIFIED CHINESE_CHINA.ZHS16GBK
  4. tnsnames.ora和sqlnet.ora文件,这两个文件可以在所要访问的Oracle数据库服务器的$ORACLE_HOME/network/admin目录下找到,把tnsnames.ora中的服务器主机名改为ip地址即可。需更改时注意备份原来的文件。
  5. 配置完毕后进入C:\Oracle运行sqlplus.exe登陆对应的数据库测试是否设置正确。在windows下使用instantclient时,需要将instantclient的sdk包中的include和lib加到工程中。
  [b]Unix平台下instantclient的配置和使用:[/b]
  本例中使用solaris_x86_10.2.0.2为客户端
  1. 将instantclient_solaris_x86_10.2.0.2中的basic、sqlplus和sdk解压至同一目录,用chmod将该目录下的所有文件设为可读写,比如:chmod
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值