OTL使用指南

OTL是一个C++数据库模板库,支持多种数据库,包括Oracle、MS SQL Server等。文档介绍了OTL的编译、基本使用、流概念、SQL绑定、迭代器、资源池、大型对象操作、国际化、Reference Cursor流以及最佳实践。特别强调了流缓冲池和大型对象(LOB)的处理,以及如何避免SQL执行后立即提交事务。文档还包含了性能对比和最佳缓冲区大小设置的建议。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

                              

 

 

 

 

 

 

OTL使用指南

 

 

 

 

 

 

 

 

 

 

 

 

 


 

 

 

 

 

 

 

文档修改历史

日期

版本

作者

修改内容

评审号

变更控制号

发布日期

2007-9-14

0.1

佘彪

初稿

 

 

 

2007-9-18

0.2

佘彪

根据9月17日上午周例会上陈彰的要求,增加编程实践相关的内容,主要通过总结OCS租费开发中OTL的使用经验,整理出13章最佳实践。

 

 

 

2007-11-8

  0.3

佘彪

增加13.3小节与开源项目ORAPP的性能对比

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


目    录

1 OTL简介............................................................................................................................................................................... 5

2 编译OTL.............................................................................................................................................................................. 5

3 基本使用............................................................................................................................................................................... 5

4 OTL流的概念................................................................................................................................................................... 14

5 主要类及方法说明.......................................................................................................................................................... 15

4.1 otl_stream的主要方法......................................................................................................................................... 16

4.2 otl_connect的主要方法...................................................................................................................................... 19

6 SQL的变量绑定和常量SQL........................................................................................................................................ 22

6.1 SQL的变量绑定......................................................................................................................................................... 22

6.2 常量SQL..................................................................................................................................................................... 25

7迭代器.................................................................................................................................................................................. 25

7.1 OTL流的读迭代器.................................................................................................................................................... 25

7.2 STL兼容的迭代器..................................................................................................................................................... 29

8 资源池................................................................................................................................................................................. 33

8.1 连接缓冲池................................................................................................................................................................. 33

8.2 OTL流缓冲池............................................................................................................................................................. 37

9 操作大型对象................................................................................................................................................................... 44

9.1大型对象的存储......................................................................................................................................................... 44

9.1.1 otl_long_string.................................................................................................................................................. 44

9.1.2 otl_long_unicode_string................................................................................................................................. 45

9.2 大型对象的读写......................................................................................................................................................... 45

10国际化................................................................................................................................................................................ 50

10.1 使用UNICODE字符串.......................................................................................................................................... 50

10.2 使用UTF8字符串................................................................................................................................................... 54

11 Reference Cursor流........................................................................................................................................... 58

12 杂项................................................................................................................................................................................... 62

12.1 使用otl_nocommit_stream避免SQL执行成功后立刻提交事务........................................................ 62

12.2 SELECT中的数据类型映射覆写......................................................................................................................... 65

12.3 使用OTL tracing跟踪OTL的方法调用........................................................................................................ 69

12.4 获取已处理行数(Rows Processed Count).................................................................................................... 76

12.5 使用otl_connect的重载运算符<<,<<=, >>................................................................................................. 78

12.6 手工刷新otl_stream缓冲区............................................................................................................................ 82

12.7 忽略INSERT操作时的重复键异常.................................................................................................................... 87

12.8 使用模板otl_value<T>创建数据容器........................................................................................................... 91

12.9 使用OTL流的读迭代器遍历流返回的ReferenceCursor....................................................................... 94

12.10使用Reference Cursor流从存储过程中返回多个Referece Cursor............................................... 98

13 最佳实践........................................................................................................................................................................ 103

13.1流缓冲区大小的设置............................................................................................................................................ 103

13.2批量操作注意的问题............................................................................................................................................ 106

13.3与开源项目ORAPP的性能对比........................................................................................................................ 107

 

 


1 OTL简介

OTL 是 Oracle, Odbcand DB2-CLI Template Library 的缩写,是一个C++编译中操控关系数据库的模板库,它目前几乎支持当前所有的各种主流数据库,例如Oracle, MS SQL Server, Sybase, Informix, MySQL, DB2, Interbase /Firebird, PostgreSQL, SQLite, SAP/DB, TimesTen, MS ACCESS等等。

OTL中直接操作Oracle主要是通过Oracle提供的OCI接口进行,操作DB2数据库则是通过CLI接口进行,至于MS的数据库和其它一些数据库,OTL只提供了ODBC的操作方式。当然Oracle和DB2也可以由OTL间接使用ODBC的方式进行操纵。

在MS Windows and Unix 平台下,OTL目前支持的数据库版本主要有:Oracle 7 (直接使用 OCI7), Oracle 8 (直接使用 OCI8), Oracle 8i (直接使用OCI8i), Oracle 9i (直接使用OCI9i), Oracle 10g (直接使用OCI10g), DB2 (直接使用DB2 CLI), ODBC 3.x ,ODBC 2.5。目前OTL的最新版本为4.0,参见http://otl.sourceforge.net/,下载地址http://otl.sourceforge.net/otlv4_h.zip

2 编译OTL

OTL是一个集成库,它包含了一个模板流框架(template stream framework)以及适配OCI7, OCI8, OCI8i, OCI9i, OCI10g, ODBC 2.5, ODBC 3.x, DB2 CLI和Informix CLI的适配器(OTL-adapters)。编译时需要使用相应的宏定义向编译器指明底层数据库API的类型。例如,如果底层使用ORACLE10g的API,则需要使用宏定义”#defineOTL_ORA10G”。

另外,也可以使用相应的宏定义控制编译器对OTL的编译。 例如,如果需要和ACE库一起编译可以使用宏定义”#defineOTL_ACE”, 如果需要OTL为所分配并处理的字符串以空字符结尾成为C风格字符串则可以使用宏定义”#define OTL_ADD_NULL_TERMINATOR_TO_STRING_SIZE”等。

所有的相关宏请参见http://otl.sourceforge.net/otl3_compile.htm

3 基本使用

OTL的一般使用步骤包括:

(1)   使用宏指明底层数据库API类型和控制编译器对OTL的编译。例如:

    #define OTL_ORA9I      // Compile OTL 4.0/OCI9i

#define OTL_UNICODE    //Enable Unicode OTL for OCI9i

(2)   创建otl_connect对象,该对象一般为全局共享的。

(3)   调用otl_connect的静态方法otl_initialize()初始化OTL环境。

(4)   调用otl_connect的rlogon()方法连接数据库。

(5)   创建otl_stream()对象,该对象一般为局部的。

(6)   调用otl_stream的open()方法打开SQL进行解析。

(7)   使用otl_stream的<<操作符绑定SQL中的变量。

(8)   使用otl_stream的>>操作符读取返回结果。

(9)   调用otl_connect的logoff()方法从数据库断开。

下面将通过一个较为全面的示例说明使用OTL连接数据库、创建表和存储过程、调用存储过程、查询记录以及插入记录、从数据库断开的具体代码实现。

#include <stdio.h>

#include <string.h>

#include <iostream>

#include <vector>

 

#define OTL_ORA9I       // Compile OTL 4.0/OCI9i

//#define OTL_UNICODE   // Enable Unicode OTL for OCI9i

#include "otlv4.h"          // include the OTL 4.0 header file

 

using namespace std;

 

/**

  *连接数据库

  */

int OTLConnect (const char* pszConnStr, otl_connect& db)

{

       try

       {

              otl_connect::otl_initialize(); // initialize OCI environment

              db.rlogon(pszConnStr);

              db.auto_commit_off ( );

              printf ( "CONNECT: OK!\n" );

       }

       catch(otl_exception& p)

       {   // intercept OTL exceptions

              printf ( "Connect Error: (%s) (%s) (%s)\n",p.msg, p.stm_text, p.var_info );

              return -1;

       }

       return 0;

}

 

/**

  *从数据库断开

  */

int OTLDisconnect (otl_connect& db)

{

       db.commit ( );

       db.logoff();

 

       printf ( "DISCONNECT: OK!\n" );

       return 0;

}

 

/**

  *创建数据库表和存储过程

  */

int OTLExec ( otl_connect& db)

{

       try

       {

              int nCnt = 0;

              char strSql[] = "SELECT count(0) FROM user_tables "

                                           " WHERE table_name = 'TEST_FTP' ";

 

              otl_stream otlCur (1, (const char*)strSql, db );

              otlCur >> nCnt;

 

              if ( nCnt == 0 )

              {

                     char strDDL[] =

                            "create table TEST_FTP                 "

                            "(                                                                          "

                            "  AREA_ID         VARCHAR2(100) not null,     "

                            "  FTP_FILE_NAME   VARCHAR2(100) not null,    "

                            "  FTP_TIME        VARCHAR2(14),               "

                            "  FTP_BEGIN_TIME  VARCHAR2(14),                   "

                            "  FTP_END_TIME    VARCHAR2(14),                   "

                            "  FTP_MOD_TIME    date,                                 "

                            "  FTP_SIZE        NUMBER(8),                      "

                            "  FTP_SOURCE_PATH VARCHAR2(100),                 "

                            "  FTP_LOCAL_PATH  VARCHAR2(100),                "

                            "  FTP_RESULT      VARCHAR2(4),                       "

                            "  FTP_REDO        VARCHAR2(1)                       )";

 

                     otl_cursor::direct_exec ( db, (const char*)strDDL );

              }

 

              char strSqlProc[] = "SELECT count(0) from user_objects "

                             " WHERE object_type = 'PROCEDURE' and object_name = 'PR_REMOVE_FTP' ";

              otl_stream otlCurProc (1, (const char*)strSqlProc, db );

              otlCurProc >> nCnt;

 

              if ( nCnt == 0 )

              {

                     char strProc[] =

                     "CREATE OR REPLACE procedure pr_remove_ftp             "

                     "      ( area in varchar2, out_flag out varchar )      "

                     "AS                                                                                    "

                     "strtmp varchar2(32);                                                  "

                     "BEGIN                                                                                    "

                     "  strtmp := area||'%';                                                        "

                     "  DELETE FROM TEST_FTP where area_id LIKE strtmp;       "

                     "  out_flag := 'OK';                                                   "

                     "END;                                                                                ";

 

                     otl_cursor::direct_exec ( db, (const char*)strProc );

              }

 

       }

       catch(otl_exception& p)

       {   // intercept OTL exceptions

              printf ( "EXECUTE Error: (%s) (%s) (%s)\n",p.msg, p.stm_text, p.var_info );

       }

       return 0;

}

 

/**

  *调用存储过程

  */

int OTLProcedure (otl_connect& db )

{

       try

       {

 

              char szData[64],  szData1[64],  szData2[64], szData3[64];

              int nSize = 0;

              char strSql[] = " BEGIN "

                                     "  pr_remove_ftp ( :area<char[100],in>, :out<char[100],out> ); "

                                     " END; ";

              otl_stream otlCur (1, (const char*)strSql, db );

              otlCur.set_commit ( 0 );

 

              strcpy ( szData, "AREA" );

              memset ( szData1, 0, sizeof(szData1) );

              memset ( szData2, 0, sizeof(szData2) );

              memset ( szData3, 0, sizeof(szData3) );

 

              otlCur << szData;

              otlCur >> szData1;

 

              printf ( "PROCEDURE: %s!\n", szData1 );

       }

       catch(otl_exception& p)

       { // intercept OTL exceptions

              printf ( "PROCEDURE Error: (%s) (%s) (%s)\n",p.msg, p.stm_text, p.var_info );

       }

       return 0;

}

 

/**

  *查询记录

  */

int OTLSelect (otl_connect& db)

{

       try

       {

              char szData[64],  szData1[64],  szData2[64], szData3[64], szRedo[2];

              int nSize;

              char strSql[] = " SELECT area_id, ftp_time, ftp_file_name, "

                                     " to_char(ftp_mod_time, 'YYYY-MM-DD HH24:MI:SS'), ftp_size "

                                     "  FROM TEST_FTP "

                                     " WHERE ftp_redo = :ftp_redo<char[2]>" ;

              otl_stream otlCur (1, (const char*)strSql, db );

 

              strcpy ( szRedo, "Y" );

              otlCur << szRedo;

              while ( !otlCur.eof() )

              {

                     memset ( szData, 0, sizeof(szData) );

                     otlCur >> szData;

                     otlCur >> szData1;

                     otlCur >> szData2;

                     otlCur >> szData3;

                     otlCur >> nSize;

                     printf ( "SELECT: (%s %s %s %s %d)\n",

                                    szData, szData1, szData2, szData3, nSize );

              }

       }

       catch(otl_exception& p)

       { // intercept OTL exceptions

              printf ( "Select Error: (%s) (%s) (%s)\n",p.msg, p.stm_text, p.var_info );

       }

       return 0;

}

 

/**

  *插入记录

  */

int OTLInsert (otl_connect& db)

{

       try

       {

              char szData[64],  szData1[64],  szData2[9], szData3[64], szRedo[2];

              int nSize;

              char strSql[] = " INSERT into TEST_FTP "

                                   " ( area_id, ftp_file_name, ftp_time, ftp_mod_time, ftp_size, ftp_redo )"

                                   " VALUES ( :area_id<char[100]>, "

                                   "      :ftp_file_name<char[100]>, "

                                   "      to_char(sysdate,'YYYYMMDDHH24MISS'), "

                                   "   to_date(:ftp_mod_time<char[20]>,'YYYYMMDD'), "

                                   "      :ftp_size<int>, "

                                   "   :ftp_redo<char[2]> ) ";

              otl_stream otlCur (1, (const char*)strSql, db );

 

              otlCur.set_commit ( 0 );

 

              for ( int i = 1; i < 10; i ++ )

              {

                     sprintf ( szData, "AREA_%d", i );

                     sprintf ( szData1, "FILE_NAME_%d", i );

                     if ( i < 5 )

                     {

                            sprintf ( szData2, "20070415" );

                            strcpy ( szRedo, "Y" );

                     }

                     else

                     {

                            sprintf ( szData2, "20070416" );

                            strcpy ( szRedo, "N" );

                     }

 

                     memset ( szData3, 0, sizeof(szData3) );

                     nSize = i * 100;

 

                     otlCur << szData << szData1 << szData2 << nSize << szRedo;

              }

 

              printf ( "INSERT: OK!\n" );

       }

       catch(otl_exception& p)

       { // intercept OTL exceptions

              printf ( "INSERT Error: (%s) (%s) (%s)\n",p.msg, p.stm_text, p.var_info );

       }

       return 0;

}

 

/**

  *主函数

  */

int main ( int argc, char *argv[] )

{

       otl_connect db;

       char szConn[64];

 

 

       if ( argc >= 2 )

              strcpy ( szConn, argv[1] );

       else

       {

              printf ( "otltest conn_str" );

              return -1;

       }

 

       if ( OTLConnect ( szConn, db ) < 0 )

              return 0;

       OTLExec ( db );

       OTLProcedure ( db );

       OTLInsert ( db );

       OTLSelect ( db );

       OTLDisconnect ( db );

 

       return 0;

}

4 OTL流的概念

OTL设计者认为,任何SQL语句、PL/SQL块或存储过程调用都被输入和输出变量特征化。例如:

l       一个SELECT语句在其WHERE子句中拥有标量的输入变量,而在其SELECT子句则定义了输出的列,如果SELECT语句返回的是多行记录则输出列是个向量参数。

l       一个INSERT和UPDATE语句需要将数据写入表中,它们拥有输入参数。另外,一个DELETE语句由于需要指明删除记录的类型,同样拥有输入。工业强度的数据库服务器通常也支持批量操作,例如批量的查询、更新、删除和插入,因此INSERT/UPDATE/DELETE语句的参数在批量操作的情况下也可能是向量。

l       一个存储过程可能含有输入和(或)输出参数。通常存储过程的参数是标量,但是也有特例,例如返回的是引用游标(Oracle)或者记录集(MS SQL SERVER或者Sybase)。

l       一个PL/SQL块可能含有输入和(或)输出参数,这些参数可能是标量也可能是向量。

图4-1 OTL的流

因此,任何的SQL或者其程序上的扩展在交互过程中都可以如图4-1所示看作拥有输入和输出的黑盒。OTL通过将数据流和SQL的概念联合起来,用otl_stream类表达这种抽象。

由于SQL语句可能以批量的方式执行,otl_stream是一个缓冲流。它拥有两个独立的缓冲区:输入和输出。输入缓冲区由所有集中到一起的输入参数组成,输出缓冲区则由所有集中到一起的输出变量组成。

OTL流和C++的缓冲流很相似。一个SQL语句或存储过程调用被当作一个普通的缓冲流被打开。OTL流的操作逻辑和C++流操作逻辑基本相同,但是OTL流的输出和输出缓冲区可能重叠。

OTL流拥有flush()方法在输入缓冲区写满的时候将其自动刷新,也含有一系列的<<和>>运算符来读和写不同数据类型的对象。它最重要的优点是为任何类型的SQL语句和存储过程调用提供了统一的接口。应用开发者能够通过熟悉少量的语法和函数名称像使用C++流一样来使用OTL流。

在OTL流的内部拥有一个小型的解析器来解析所声明的绑定变量以及绑定变量的数据类型。因此,免去了使用特殊的绑定函数来绑定已声明的C/C++主机变量(hostvariables)。由于所有必须的缓冲区在OTL流中会自动创建,因此OTL仅仅需要被打开来进行读和写相应的数值。

OTL流接口要求使用OTL异常。OTL流操作都能可能抛掷otl_exception异常。因此为了拦截异常并阻止程序异常终止,必须使用try/catch块来包裹OTL流的使用代码。

OTL流的实现otl_stream具有较高的自动化功能,当OTL流的所有的输入变量被定义好(也就是输入缓冲区被填满),它会触发OTL流中的黑盒来执行。在黑盒执行的过程中输出缓冲区被填充。在执行完成后,输出缓冲区中的值能够从OTL流中被读取。如果执行的是一个SELECT语句并且返回多于输出缓冲区大小的行,那么在输出缓冲区的内容被读取后,OTL会自动读取下一批行记录到输出缓冲区。

5 主要类及方法说明

5-1 OTL主要类说明

类名

说明

otl_connect

负责创建和处理连接对象以及事务管理。

otl_stream

OTL流概念(参见第4小节)的具体实现。任何具有输入输出的SQL语句,匿名的PL/SQL块或者存储过程能够使用otl_stream类进行C++编程。

一般传统的数据库API拥有绑定主机变量到SQL语句中占位符的函数。因此,开发者需要在程序中声明host array,解析SQL语句,调用绑定函数,填充输入变量,执行SQL语句,读输出变量等。这些操作结束后又继续填充输入变量,执行SQL语句,读输出变量。

以上的所有事情能够在otl_stream中全部自动完成。otl_stream在保证性能的情况下提供了完全自动的与数据库的交互。

otl_stream的性能主要被缓冲区大小arr_size一个参数控制。缓冲区大小定义了插入表的逻辑行以及与数据库一次往反交互(one round-trip to the database)过程中从表或视图中查询的逻辑行。

otl_exception

可能代表数据库错误也可能代表OTL自身的错误。OTL函数如果在使用底层的数据库API时返回非0的错误码,则会产生otl_exception类型的异常。

4.1otl_stream的主要方法

5-2 类otl_stream的主要方法说明

主要方法

说明

otl_stream(

const int arr_size,

     const char* sqlstm,
     otl_connect& db,

     const char* ref_cur_placeholder=0,
     const char* sqlstm_label=0

);

构造函数,负责创建otl_stream对象并调用open()方法。

参数arr_size为流的缓冲区大小,

参数db为otl_connect连接对象的引用,

参数ref_cur_placeholder为reference cursor的占位符名称,

参数sqlstm_label为SQL语句的标签,用于取代异常描述消息otl_exception::stm中默认填充的SQL语句。

void open(
const int arr_size,

     const char* sqlstm,
     otl_connect& db,

     const char* ref_cur_placeholder=0,
     const char* sqlstm_label=0

);

打开SQL语句对其进行解析,所有的输入和输出变量都在流内部别动态分配,并且自动绑定到占位符。

void close(void);

#ifdef OTL_STREAM_POOLING_ON
 void close(

constbool save_in_stream_pool=true

);
#endif

关闭流。如果使用了流缓冲池,则可以使用带save_in_stream_pool参数的close()函数。如果参数save_in_stream_pool为true则流并不会真正关闭,而是被缓冲池回收。

int good(void);

测试流是否打开。

int eof(void);

测试是否所有的数据都已经从流中读取。

void rewind(void);

重绕流。如果流不含有任何输入变量,该方法将强制流执行SQL语句。

operator int(void);

流到int的转换操作符,返回从!eof()获得的流状态。它允许运算符>>返回!EOF的流状态并且能够在while()循环中像下面的代码那样使用:

while(s>>f1>>f2) {
  cout<<”f1=”<<f1<<”, f2=”<<f2<<endl;

}

 int is_null(void);

判断是否从流中获得NULL

void flush(void);
void flush // OTL/OCI8,8i,9i,10g only 
(

const int row_offset=0,                             

const bool force_flush=false

);

刷新流的输出缓冲区。这实际上意味着批量执行SQL语句,其执行的批量和流输出缓冲区中已经填充的SQL数相等。当输出缓冲区被填满时,缓冲区将被自动刷新。如果流的auto_commit标志被置上,则在刷新完毕后当前事务被提交。

OTL/OCI8,8i,9i,10g拥有另外一个版本的flush()方法。能够通过参数row_offset指定缓冲区刷新的开始位置,通过参数force_flush则能够指定是否在出现错误抛出otl_exception的情况下仍然强制继续刷新,忽略之前的错误。

long get_rpc(void);

获得处理的行数(Rows Processed Count, RPC)。

void set_column_type(

const int column_ndx,
const int col_type,

const int col_size=0

);

设置SELECT输出列的数据类型。

参数column_ndx为列的索引,如1,2,3…

参数col_type为OTL定义的数据类型常量(参见11.2小节)

参数col_size为新数据类型的大小,只被otl_var_char类型使用。

void set_commit(int auto_commit=0);

设置流的auto_commit标志。默认情况下,该标志被置上,即当输出缓冲区刷新时,当前的事务被自动提交。

注意流的auto_commit标志和数据库库的自动提交模型没有任何关系。

如果需要默认方式为不自动提交的流,则可以使用otl_stream的子类otl_nocommit_stream。

void set_flush(const bool auto_flush=true);

设置auto_flush标志。默认情况下auto_flush的值为true, 即如果缓冲区出现脏数据则在流的析构函数中刷新缓冲区。如果自动刷新标志被关闭,则需要使用close()方法或者flush()方法对流进行刷新。

注意该函数仅仅能够设置流的析构函数中是否自动刷新,并不是通常意义上的缓冲区刷新。

int setBufSize(const int buf_size);     

设置流缓冲区的大小。

4.2 otl_connect的主要方法

4-3 类otl_connect的主要方法说明

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值