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的主要方法说明
4.2 otl_connect的主要方法
4-3 类otl_connect的主要方法说明