OTL 是 Oracle, Odbc and DB2-CLI Template Library 的缩写,是一个 C++ 编译中操控关系数据库的模板库,它目前几乎支持所有的当前各种主流数据库。 OTL 使用起来比较方便,其官方网站也提供了详细的文档和例子( http://otl.sourceforge.net/otl3.htmhttp://otl.sourceforge.net/otl3_examples.htm )。
最近在项目中需要使用 OTL 调用 Oracle 存储过程,并且需要返回游标,在网上查了一下,相关的中文资料比较少,经过一番辛苦的查找和摸索,最后终于如愿达到目的。在这里把相关的经验分享给大家,如有不对的地方,欢迎拍砖。
OTL 调用 Oracle 存储过程,其官方文档中给出了相应的例子,地址是 http://otl.sourceforge.net/otl4_ex149.htm 。现节选关键部分如下:
…
otl_stream i(1,
"begin "
" my_pkg.my_proc(:f1<int,in>,:f2<int,in>, "
" :str1<char[100],out>, "
" :cur1<refcur,out[50]>, "
" :cur2<refcur,out[50]>); "
"end;",
db // connect object
);
i.set_commit(0); // set stream "auto-commit" to OFF.
char str1[101];
float f1;
char f2[31];
otl_refcur_stream s1, s2; // reference cursor streams for reading rows.
i<<8<<4;
i>>str1;
i>>s1;
i>>s2;
cout<<"STR1="<<str1<<endl;
cout<<"=====> Reading :cur1..."<<endl;
while(!s1.eof()){ // while not end-of-data
s1>>f1>>f2;
cout<<"f1="<<f1<<", f2="<<f2<<endl;
}
cout<<"=====> Reading :cur2..."<<endl;
while(!s2.eof()){ // while not end-of-data
s2>>f1>>f2;
cout<<"f1="<<f1<<", f2="<<f2<<endl;
}
s1.close(); // closing the reference cursor
s2.close(); // closing the reference cursor
…
可以看出,调用存储过程和执行普通的 sql 大同小异,也是通过 otl_stream 对象绑定相关参数,不同之处有以下几点:
1 .调用存储过程 / 函数时,缓冲大小必须设置为 1 。
2 . Sql 声明时语句必须采用 ”begin …… end;” 的形式。存储过程如果位于包内,还必须带上包名。绑定参数除了声明类型、大小以外,还必须指明参数的输入输出标志。返回游标的大小代表了游标内的记录数。
3 .返回游标绑定的数据类型是 otl_refcur_stream ,就像普通的 otl_stream 一样,可以使用 while 循环读取其中的数据。游标对象使用完毕后,别忘了使用 close 方法将其关闭。
在这个过程中,有两个需要注意的地方:
1 . OTL 在输入输出参数的方向标志方面稍微有点不人性化,即要求所有的 in 、 out 与前面的逗号“ , ”之间不允许有空格、制表符等空白字符,否则就会无法识别方向标志而发生 ORA-01008 错误,并提示“并非所有变量都已绑定”。
2 .如果存储过程内存在多条执行路径,并且某些执行路径并不返回游标的情况下,就需要程序员自己判断在当前情况下是否需要接收游标。如果当前存储过程并未返回游标,而在 c++ 程序中执行了提取( >> )操作,就会发生 ORA-24338 错误 ,并提示“未执行语句句柄”。此时,如果允许的话可以通过存储过程返回的其他变量判断是否需要进行接收操作,如:
…
if(“00” == ret_code) // 其中 ret_code 是存接收了储过程执行状态的变量
{
o >> first_cur;
o >> detail_cur;
}
…
OTL 调用存储函数和存储过程基本相似,只不过是多了一个返回值而以。格式大致如下:
…
otl_stream i(1,
"begin "
" :rc<int,out> :=my_pkg. my_func(:f1<int,in>,:f2<int,in>, "
" :str1<char[100],out>, "
" :cur1<refcur,out[50]>, "
" :cur2<refcur,out[50]> "
" ); "
" end; ",
db // connect object
);
int res = 0;
char str1[101];
float f1;
char f2[31];
otl_refcur_stream s1, s2; // reference cursor streams for reading rows.
i<<8<<4;
i >> res;
i>>str1;
i>>s1;
i>>s2;
…
这里也有两个需要注意的地方,一是 sql 声明时存储函数名前面的等号要用“ := ”;再就是接收输出时,首先接收函数返回值,再依次接收其他输出。其他操作同存储过程一致。关于调用存储过程 / 函数时, sql 声明的具体格式, OTL 提供了一个专门的函数create_stored_proc_call ,可以生成相应的绑定 sql 。其具体使用方法参见 http://otl.sourceforge.net/otl4_ex153.htm 。
补充一下:笔者的环境是 otl v4.0.218 OraClient11g vs2008sp1 winxp sp3 。