前言
这篇文章是《Weblogic与Tuxedo互连指南》的延续,Tuxedo是交易中间件,我手上的项目是用来接受请求后再转发到另一个Tuxedo中的环境模式。中心转发时需要记录日志以及一些业务逻辑处理,需要与数据库交互,中心是用c++写的,使用的occi与Oracle连接。数据传递使用XML。那么现在问题来了,当客户端传递大xml到中心时,Tuxedo服务进程就died。我是Java程序员,在这之前对c++只有HelloWorld的接触,指针也只是听说过。各种调试后找到了服务died的原因,原因在于与数据库交互时,数据量大的原因,导致服务挂起,数据库过程定义入参是clob,但在c++里面set的string,好吧,以上全是环境铺垫,下面进入正题,occi与oracle的clob传入及传出。
步骤
1、定义Oracle过程,传入和传出都使用CLOB
2、编写C++代码调用Oracle过程,实现CLOB传入及传出
实现Oracle部分
1、建表
create table TESTCLOB
(
CID NUMBER(38),
DETAIL CLOB,
WRITEDATE DATE
);
2、建过程
CREATE OR REPLACE PACKAGE Pkg_test IS
PROCEDURE p_test_clob(prm_input IN CLOB, --传入
prm_output OUT CLOB, --传出
prm_lengthb OUT INTEGER); --传出长度
END Pkg_test;
CREATE OR REPLACE PACKAGE BODY Pkg_test IS
PROCEDURE p_test_clob(prm_input IN CLOB, -- 传入
prm_output OUT CLOB, -- 传出
prm_lengthb OUT INTEGER)-- 传出长度
AS
clob_buffer CLOB; --缓冲区
input CLOB; --input分段
n_out VARCHAR2(100);
n_length INTEGER;
input_length INTEGER; --input长度
base_length INTEGER; --基本分段的长度
index_length INTEGER;
BEGIN
prm_lengthb := 0; --初始化传出长度为0
base_length := 16383; --每段长度16383
index_length := 16383; --第一段长度16383
/* 写入处理 */
--1、清空clob字段
update testclob
set detail = empty_clob()
where cid = '1';
--2、将clob写给缓冲区
select detail
into clob_buffer
from testclob
where cid = '1';
--3、截取input,一次写入过大会报错,故截取多次写入
input_length := DBMS_LOB.getlength(prm_input);
input := dbms_lob.substr(prm_input,base_length,1);
--4、写入第一段
DBMS_LOB.OPEN(clob_buffer, DBMS_LOB.LOB_READWRITE);
DBMS_LOB.WRITE(clob_buffer, DBMS_LOB.getlength(input), 1, input);
--5、如果input超过第一段长度就分段截取写入,每次16383
while input_length > index_length loop
input := dbms_lob.substr(prm_input,base_length,index_length);
index_length := index_length + base_length;
DBMS_LOB.WRITEAPPEND(clob_buffer,DBMS_LOB.getlength(input), input);
end loop;
--6、写入表字段
update testclob
set detail = clob_buffer
where cid = '1';
--7、关闭缓存区
DBMS_LOB.CLOSE(clob_buffer);
/* 传出处理 */
--1、创建临时空间
dbms_lob.createtemporary(prm_output, true);
--2、往prm_output写入‘Hi,你好’,200次
n_out := 'Hi,你好!';
--3、关键点,获取字节长度,C++中使用
n_length := lengthb(n_out);
for i in 1..200 loop
dbms_lob.writeappend(prm_output, length(n_out), n_out);
--4、累加写入长度
prm_lengthb := prm_lengthb + n_length;
end loop;
END p_test_clob;
END Pkg_test;
实现C++部分
#include "stdafx.h"
#include <iostream>
#include <occi.h>
using namespace std;
using namespace oracle::occi;
#pragma comment (lib, "C:\\oracle\\product\\10.2.0\\db_1\\OCI\\lib\\MSVC\\oraocci10.lib")
int main(){
Environment *env=Environment::createEnvironment(Environment::DEFAULT);
string name = "test";
string pass = "test";
string srvName = "192.168.43.1:1521/orcl";
string lstr_input = "HelloWorld";
Statement *stmt;
try{
//1、连接数据库
Connection *conn = env->createConnection(name, pass,srvName);
cout<<"conn success"<<endl;
//2、执行一个oracle方法,创建clob的临时空间,与oracle过程里面的用法一致
stmt = conn->createStatement("begin dbms_lob.createtemporary(:p1,FALSE);end;");
stmt->registerOutParam(1,OCCICLOB);
stmt->executeUpdate();
//3、取出上一步的对象
Clob clob_input = stmt->getClob(1);
//4、写入字符传到clob
clob_input.write((unsigned int)lstr_input.size(),
(unsigned char*)lstr_input.c_str(),(unsigned int)lstr_input.size());
conn->terminateStatement (stmt);
//5、调用业务过程
stmt = conn->createStatement("BEGIN Pkg_test.p_test_clob(:v1,:v2,:v3); END;");
stmt->setClob(1,clob_input); //传入包含字符串的clob
stmt->registerOutParam(2,OCCICLOB);
stmt->registerOutParam(3,OCCIINT);
stmt->executeUpdate ();
//6、取出clob
Clob clob_ouput = stmt->getClob(2);
//返回的字符数
unsigned int cloblen = clob_ouput.length();
//7、分配空间用于存储返回的clob中的字符
// 空间大小为过程返回的第3个参数,即过程中累加的字节数
char *chr_out = (char *) malloc(stmt->getInt(3));
//8、设置字符类型,避免中文乱码
clob_ouput.setCharSetForm(OCCI_SQLCS_IMPLICIT);
//9、读取clob中的数据到内存空间
clob_ouput.open(OCCI_LOB_READONLY);
clob_ouput.read(cloblen*2, (unsigned char*)chr_out, cloblen*2, 1);
clob_ouput.close();
//10、重要的一步,调试了几天才明白,char的结尾加'\0'
// 关键是结尾的位置不是cloblen的字符长度,而是过程返回的字节长度
// Java中没有这些问题,纠结了好长时间。没有'\0'结尾时读取的值最后
// 有乱码,若给的下标位置没正确,则读取的字符被截断
chr_out[stmt->getInt(3)]='\0';
string str_ouput = chr_out;
cout<<str_ouput.c_str()<<endl;
int j;
cin>>j;
conn->terminateStatement (stmt);
env->terminateConnection(conn);
}
catch(SQLException e)
{
cout<<e.what()<<endl;
return -1;
}
Environment::terminateEnvironment(env);
cout<<"end!"<<endl;
return 0;
}
测试
若没有chr_out[stmt->getInt(3)]='\0';,则最后有乱码
若'\0’赋值的下标不对,则数据被截取