OTL资料简编
1. 序
本文作为自己对OTL学习的备忘而记录,同时希望能给你带来帮助,如有任何建议及意见欢迎mail至nivana999@163.com。
由于自己比较懒,已经有的东西就不打算再重来一遍,所以摘抄了一些网上现有的资料,比如1,2节就是对一些网文的copy,在这里只是用我自己习惯的方式重新排了一下,为了表示对原作者的尊重,在参考资料里已列出了原文出处或链接。其中出于自己对otl的理解或思维习惯作了少部分的增删和修改。“实例代码”一节列出了一个简单的otl使用范例,仅包括了数据库操作的最基础部分,为了查阅起来简单明了,代码中未加过多的错误处理,目的仅是为了说明一个操作的必要步骤。
钟磊
2009-5-21于云南通其科技
2. 简介
2.1. OTL介绍
OTL 是 Oracle, Odbc and 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.193,参见http://otl.sourceforge.net/ 其中有对otl的介绍、源码下载、例子及文档、FAQ、作者的简历和一些有用的链接。
优点:
a. 跨操作系统跨数据库
b. 运行效率高,与C语言直接调用API相当
c. 开发效率高,起码比ADO.net使用起来更简单,更简洁,整个库就只有一个头文件
d. 部署容易,不需要ADO组件,不需要framework.net 等
缺点:
a. 说明文档以及范例不足够丰富(暂时性的)
注意OTL的跨操作系统及跨数据库特性是由其调用具体数据库的接口而决定的,比如oracle可以通过OCI接口、db2可通过CLI接口。另外的一些数据库则是通过ODBC访问的,比如我需要在操作系统A上通过OTL访问数据库B,要么这个数据库满足otl特定的调用接口(比如oracle或db2),要么在此操作系统上存在odbc使得otl可以通过odbc来调用此数据库。
2.2. OTL的使用
OTL使用起来也很简单,使用不同的数据库连接,主要是根据需要在程序开始的宏定义来指定的。 OTL是首先根据这个宏定义来初始化数据库连接环境。 OTL中用来区分连接方式的宏定义主要有下面这些:
OTL_ORA7, OTL_ORA8, OTL_ODBC, OTL_DB2_CLI, OTL_ODBC_MYSQL...
不同的宏对应的数据库API,具体说明如下:
宏定义名 |
说明 |
OTL_DB2_CLI |
for DB2 Call Level Interface (CLI) |
OTL_INFORMIX_CLI |
for Informix Call Level Interface for Unix (when OTL_ODBC_UNIX is |
OTL_IODBC_BSD |
for ODBC on BSD Unix, when iODBC package is used |
OTL_ODBC |
for ODBC |
OTL_ODBC_MYSQL |
for MyODBC/MySQL. The difference between OTL_ODBC_MYSQL and |
OTL_ODBC_POSTGRESQL |
for the PostgreSQL ODBC driver 3.5 (and higher) that are connected to PostgerSQL 7.4 / 8.0 (and higher) servers. |
OTL_ODBC_UNIX |
for ODBC bridges in Unix |
OTL_ODBC_zOS |
for ODBC on IBM zOS. |
OTL_ODBC_XTG_IBASE6 |
for Interbase 6.x via XTG |
OTL_ORA7 |
for OCI7 |
OTL_ORA8 |
for OCI8 |
OTL_ORA8I |
for OCI8i |
OTL_ORA9I |
for OCI9i. All code that compiles and works under #define OTL_ORA7, OTL_ORA8, and OTL_ORA8I, should work when OTL_ORA9I is used |
OTL_ORA10G |
for OCI10g. All code that compiles and works under #define OTL_ORA7, OTL_ORA8, OTL_ORA8I, OTL_ORA9I, should work with OTL_ORA10G. |
OTL_ORA10G_R2 |
for OCI10g, Release 2 (Oracle 10.2). All code that compiles and works under #define OTL_ORA7, OTL_ORA8, OTL_ORA8I, OTL_ORA9I, and OTL_ORA10G should work with OTL_ORA10G_R2 . |
我们在编译OTL的程序时,需要使用不同的头文件和数据库API,这就要程序在编译时联接lib库文件,不同的数据库对应的lib文件所在位置各不相同,下面是分别在windows与Unix下的数据库API所需要的头文件及lib文件所在的位置列表:
API |
API header files for Windows |
API libraries for Windows |
OCI7 |
<ORACLE_HOME>/oci/include |
<ORACLE_HOME>/oci/lib/<compiler_specific>/ociw32.lib |
OCI8 |
<ORACLE_HOME>/oci/include |
<ORACLE_HOME>/oci/lib/<compiler_specific>/oci.lib |
OCI8i |
<ORACLE_HOME>/oci/include |
<ORACLE_HOME>/oci/lib/<compiler_specific>/oci.lib |
OCI9i |
<ORACLE_HOME>/oci/include |
<ORACLE_HOME>/oci/lib/<compiler_specific>/oci.lib |
OCI10g |
<ORACLE_HOME>/oci/include |
<ORACLE_HOME>/oci/lib/<compiler_specific>/oci.lib |
ODBC |
Normally, in one of the C++ compiler system directories, no need to include explicitly. |
Normally, in one of the C++ compiler system directories: odbc32.lib |
DB2 CLI |
<DB2_HOME>/include |
<DB2_HOME>/lib/db2api.lib |
API |
API header files for Unix |
API libraries for Unix |
OCI7 |
-I$(ORACLE_HOME)/rdbms/demo -I$(ORACLE_HOME)/rdbms/public |
-L$(ORACLE_HOME)/lib/ -lclntsh |
OCI8 |
-I$(ORACLE_HOME)/rdbms/demo -I$(ORACLE_HOME)/rdbms/public |
-L$(ORACLE_HOME)/lib/ -lclntsh |
OCI8i |
-I$(ORACLE_HOME)/rdbms/demo -I$(ORACLE_HOME)/rdbms/public |
-L$(ORACLE_HOME)/lib/ -lclntsh |
OCI9i |
-I$(ORACLE_HOME)/rdbms/demo -I$(ORACLE_HOME)/rdbms/public |
-L$(ORACLE_HOME)/lib/ -lclntsh |
OCI10g |
-I$(ORACLE_HOME)/rdbms/demo -I$(ORACLE_HOME)/rdbms/public |
-L$(ORACLE_HOME)/lib/ -lclntsh |
ODBC |
ODBC bridge specific |
ODBC bridge specific |
DB2 CLI |
-I/<DB2_HOME>/sqllib/include |
-L/<DB2_HOME>/sqllib/lib -ldb2 |
从上面可以看出,如果在windows下操纵MS的 数据库,使用MS VC++来编译OTL程序,就非常简单了,不用另外去找ODBC32.lib,VC的编译器中已经默认link到工程中了,具体请看如何编译OTL:http://otl.sourceforge.net/otl3_compile.htm。
如果需要在vc下编译Oracle10g的工程则如下书写即可:
///////////////////////////////////////////
#define OTL_ORA10G //这里必须在otlv4.h包含之前,otlv4.h使用了具体平台相关的宏定义
#include "..//..//otlv4_h/otlv4.h" //otl库仅有的一个头文件
#pragma comment(lib,"oci.lib") //windows下oci接口的静态导入库文件
///////////////////////////////////////////
同时在vc2003的c++工程中设置
工程->属性->C++->常规->附加包含目录为D:/oracle/product/10.1.0/db_1/OCI/include
工程->属性->连接器->常规->附加库目录为D:/oracle/product/10.1.0/db_1/OCI/lib/MSVC
注:这里我的操作系统<ORACLE_HOME>为D:/oracle/product/10.1.0/db_1/
3. 常用otl类
这里列出部分otl的常用类说明,这些资料在otl的在线文档http://otl.sourceforge.net/otl3_class.htm中有着更详细的解释,此处仅简要对其说明一下。
3.1. otl_connect类
提供数据库连接、事务操作的功能。
主要成员函数:
l int connected; // 是否已经连接到数据库
l static int otl_initialize(const int threaded_mode=0); // 初始化OTL 环境,必须在调用 OTL
API 之前被调用;threaded_mode=1表示运行在多线程环境
l void set_max_long_size(const int amax_size); // 设置缓冲区大小(仅对大数据字段适用)
l otl_connect(const char* connect_str,const int auto_commit=0); // connect_str的格式:USER/PASSWORD@TNS_ALIAS
l void rlogon(const char* connect_str,const int auto_commit=0);
l void logoff();
l void server_attach(const char* tnsname=0); // OTL/OCI8 only
l void server_detach();// OTL/OCI8 only
l session_begin(const char* username, const char* password, const int auto_commit=0);// OTL/OCI8 only
l void session_end();// OTL/OCI8 only
l void session_reopen(const int auto_commit=0)// OTL/OCI8 only,打开用 session_end 关闭的会话
l void commit();
l void rollback();
l 注释:使用 server、session来登录比用 rlogon 效率更高
l 建议使用显式的 commit 和 rollback,不使用 autocommit
3.2. otl_stream类
以宿主调用和流的方式完成 SQL 语句的执行。
工作原理:先分析程序员指定的 SQL 语句,之后以流的方式把数据和 otl_stream 内部的缓冲区作交换,通过 flush()将数据刷新到数据库中。
otl_stream( const short arr_size, otl_connect& db, const char* ref_cur_placeholder=0) ) |
构造函数 arr_size缓冲记录的条数 sqlstm 要执行的 SQL 语句 db 使用的数据库连接 ref_cur_placeholder 表示返回的光标(如果有的话)的名字 |
void open( const short arr_size, const char* sqlstm, otl_connect& db, const char* ref_cur_placeholder=0) ) |
同上 |
void close() |
关闭流 |
int good() |
测试流是否已经成功打开 |
int eof() |
是否到了流的结尾,和标准 C++ 流的 eof 相同 |
void flush() |
刷新 otl_stream 的缓冲区(获取新的数据或者把数据写到数据库中);如果是自动提交,当缓冲区满的时候,otl_stream会自动 flush。当关闭了自动提交 set_flush(false),则需要显示调用flush()。 但一般情况建议使用自动提交缓冲,只要设置合理的缓冲大小即可,即不调用set_flush(false) |
void clean(const int clean_up_error_flag=0) |
清空缓冲区而不执行刷新操作 |
int is_null() |
测试通过 >> 运算符获取的值是否为空 |
long get_rpc() |
获取最后一次刷新缓冲提交处理的记录数目,限于INSERT、DELETE、UPDATE语句 |
otl_column_desc*describe_select(int& desc_len); |
获取字段描述信息,适用于SELECT语句、引用光标(OCI)和存储过程(ODBC for MS SQL Server and Sybase)
class otl_column_desc{ public: char name[512]; // column name int dbtype; // database dependent, column datatype code. int otl_var_dbtype; // OTL defined, column datatype code int dbsize; // column length int scale; // for numeric columns, column scale int prec; // for numeric columns, column precision int nullok; // indicator whether column is nullable or not }; |
void set_commit( int auto_commit=0 ) |
刷新flush()缓冲区的时候,是否自动提交事务,默认为1。从更易于理解的角度,建议在显式提交的应用中用otl_nocommit_stream,但建议事务最好放到存储过程里做,在代码中将会变得很麻烦,代码中不再调用set_commit,使用默认自动提交为true。 |
3.3. otl的数据类型
otl_var_char |
null terminated string (code=1) |
otl_var_double |
8 byte floating point number (code=2) |
otl_var_float |
4 byte floating point number (code=3) |
otl_var_int |
32 bit signed integer (code=4) |
otl_var_unsigned_int |
32 bit unsigned integer (code=5) |
otl_var_short |
16 bit signed integer (code=6) |
otl_var_long_int |
32 signed integer 9code=7) |
otl_var_timestamp |
datatype that is mapped into TIMESTAMP_STRUCT, ODBC only (code=8) |
otl_var_varchar_long |
datatype that is mapped into LONG in Oracle 7/8, TEXT in MS SQL Server and Sybase (code=9) |
otl_var_raw_long |
datatype that is mapped into LONG RAW in Oracle 7/8, IMAGE in MS SQL Server ad Sybase (code=10) |
otl_var_clob |
datatype that is mapped into CLOB in Oracle 8 (code=11) |
otl_var_blob |
datatype that is mapped into BLOB in Oracle 8 (code=12) |
3.4. otl的错误处理
class otl_exception {
public:
char stm_text[2048]; // 导致异常的SQL 语句
char var_info[256]; // 导致数据类型不兼容的邦定变量的名字
unsigned char msg[1000]; // 数据库或者OTL的错误信息
int code; // 错误代码
};
3.5. 使用绑定变量
示例:
"insert into test_tab "
"values(:nid<int>,:f2<char[32]>);"
占位符(比如nid)可以用没有意义的f1代替,但是在一个SQL语句中不能使用相同名字的占位符,在使用如上字符串构建otl_stream对象时otl将分配绑定变量的内存,在otl_stream析构时释放。如:
otl_stream o(32, //arr_size缓冲区可容纳的记录数
"insert into test_tab "
"values(:f1<int>,:f2<char[32]>);",//OTL将动态申请32字节的内存,调用完成后释放
g_db //数据库连接对象
);
这里支持的数据类型如下表:
int |
signed int |
unsigned |
unsigned int |
short |
short int |
long |
long int |
float |
byte floating point number |
double |
byte floating point number |
timestamp |
MS SQL Server/Sybase DATETIME, Oracle DATE; requires TIMESTAMP_STRUCT (OTL/ODBC), or otl_datetime (both ODBC and OCIx) |
char[length] |
null terminated string; length is database dependent; for Oracle in [3,32545]; for ODBC it depends on the database backend and the ODBC driver |
varchar_long |
for Oracle 7: LONG; for Oracle 8: LONG; for ODBC: SQL_LONGVARCHAR |
raw_long |
for Oracle 7: LONG RAW; for Oracle 8: LONG RAW; for ODBC: SQL_LONGVARBINARY |
clob |
for Oracle 8: CLOB |
blob |
for Oracle 8; BLOB |
3.6. char类型
在执行INSERT语句的时候,如果数据库中char字段的大小是n,则这个INSERT语句的绑定变量的大小需要定义为n+1,以使得在插入刚好n个字符的情况下留出0结束的位置(c/c++中需要有零结束符,数据库中就不需要了);
例如:
INSERT INTO my_table VALUES(
:1<int>,:2<char[33]>, :3<double>,:4<char[129]>
)
这个例子中,数据库中my_table的第二和第四个字段的大小分别为32和128。
3.7. number类型
对数据库中定义的数字字段,可以根据字段的大小使用16位、32 位的整数和double类型;当然,如果对所有OCI的数字字段使用double,也不会出错。
3.8. otl_datetime
class otl_datetime{
public:
int year;
int months;
int day;
int hour;
int minute;
int second;
};
3.9. otl_null
otl_null表示一个空值,当要INSERT一个空值到数据库中时,必须使用它。
otl_null的定义如下:
class otl_null{
public:
otl_null(){}
~otl_null(){}
};
4. 实例代码
下面的代码展示了如果使用OTL连接Oracle10g并进行增、删、改、查及存储过程的调用操作,其中对需要注意的地方进行了详细注释。
编译前需要在vc2003的c++工程中设置如下:
工程->属性->C++->常规->附加包含目录为D:/oracle/product/10.1.0/db_1/OCI/include
工程->属性->连接器->常规->附加库目录为D:/oracle/product/10.1.0/db_1/OCI/lib/MSVC
注:这里我的操作系统<ORACLE_HOME>为D:/oracle/product/10.1.0/db_1/
// zhonglei
// 20090512
#include "stdafx.h"
#include <Windows.h>
#include <iostream>
using namespace std;
#include <stdio.h>
//指定Oracle10g的OCI调用
#define OTL_ORA10G
//OTL
#include "..//..//otlv4_h/otlv4.h"
#pragma comment(lib,"oci.lib")
//数据库对象
otl_connect g_db;
//普通存储过程调用
void procedure_normal(void);
//返回游标和字符串数组的存储过程调用
void procedure_cursor(void);
//创建表
int createtable(const char *sztb);
//删除表
int droptable(const char *sztb);
//插入记录
void insert(void);
//删除记录
void del(void);
//更新记录
void update(void);
//查询记录
void select(void);
//连接数据库
int connect(const char *szConnectstring);
//断开数据库
void disconnect();
int _tmain(int argc, _TCHAR* argv[])
{
//连接数据库
if(connect("testuser/unsys@test"))
return 1;
//删除表
if(droptable("test_tab"))
return 1;
//创建表
if(createtable("test_tab(f1 int,f2 varchar2(32))"))
return 1;
insert();
del();
update();
select();
//调用普通存储过程
procedure_normal();
//调用返回游标和字符串数组的存储过程
procedure_cursor();
//断开数据库连接
disconnect();
system("pause");
return 0;
}
/************************************************************************/
/*创建表
/************************************************************************/
int createtable(const char *sztb)
{
try{
char szBuf[256];
sprintf(szBuf,"create table %s",sztb);
otl_cursor::direct_exec
(
g_db,
szBuf
);
g_db.commit();
}
catch(otl_exception& p)
{
cerr<<p.msg<<endl; // print out error message
cerr<<p.stm_text<<endl; // print out SQL that caused the error
cerr<<p.sqlstate<<endl; // print out SQLSTATE message
cerr<<p.var_info<<endl; // print out the variable that caused the error
return 1;
}
printf("创建表%s/n",sztb);
return 0;
}
/************************************************************************/
/*删除表
/************************************************************************/
int droptable(const char *sztb)
{
try{
char szBuf[256];
sprintf(szBuf,"drop table %s",sztb);
otl_cursor::direct_exec
(
g_db,
szBuf,
otl_exception::disabled // 禁用异常功能
);
g_db.commit();
}
catch(otl_exception& p)
{
cerr<<p.msg<<endl; // print out error message
cerr<<p.stm_text<<endl; // print out SQL that caused the error
cerr<<p.sqlstate<<endl; // print out SQLSTATE message
cerr<<p.var_info<<endl; // print out the variable that caused the error
return 1;
}
printf("删除表:%s/n",sztb);
return 0;
}
/************************************************************************/
/*普通存储过程
/************************************************************************/
void procedure_normal(void)
{
printf(">>>普通存储过程/n");
try{
//////////////////////////////////////////////////////////////////////////
//创建存储过程
otl_cursor::direct_exec
(
g_db,
"CREATE OR REPLACE Procedure procedure_normal(P1 In Number,P2 In Out Varchar2) Is/n" //注意不要有/r,否则编译不过
"Begin/n"
"P2 := P2 || P1;/n"
"End procedure_normal;"
);
g_db.commit();
//////////////////////////////////////////////////////////////////////////
//sql字符串
char *szsql = "begin "
"procedure_normal("
":1<int,in>,"
":2<char[10240],inout>);" //通知OTL流动态申请char[n]的缓冲,调用后释放(可换作char[1024*1024*1024/*1G*/]测试观察其内存变化)
//在每次申请10241字节的情况下,循环调用此函数n次后内存都能够被及时回收
//char[]的大小在输入输出时必须保证足够的空间
"end;";
//调用存储过程第一个参数为1
otl_stream o(1,szsql,g_db);
int nPara1=999;
char szPara2[10240] = "参数二";
//顺序给输入参数赋值(从左到右)
o<<nPara1<<szPara2;
//顺序取出输出参数(从左到右)
o>>szPara2;
printf("P1=%d/nP2=%s/n",nPara1,szPara2);
printf("szPara2.Length=%d/n",strlen(szPara2));
}
catch(otl_exception& p)
{
cerr<<p.msg<<endl; // print out error message
cerr<<p.stm_text<<endl; // print out SQL that caused the error
cerr<<p.sqlstate<<endl; // print out SQLSTATE message
cerr<<p.var_info<<endl; // print out the variable that caused the error
}
}
/************************************************************************/
/*更新记录
/************************************************************************/
void update(void)
{
printf("更新记录/n");
try{
otl_stream
o(1, // buffer size
"UPDATE test_tab "
"SET f2=:f2<char[32]> "
"WHERE f1=:f1<int>",
g_db