您的位置:首页 > 数据库 > Oracle

关于 "java 如何调用Oracle存储过程中的动态refcursor结果集" 问题

2011-06-27 13:06 921 查看
关于 "java 如何调用Oracle存储过程中的动态refcursor结果集" 问题

问题追溯到:
上周末一个同事问我Java中一个PreparedStatement构造的一个动态SQL查询,如何释放资源的问题
因为查询sql是动态的,比如外层是一个循环:
for (int i=1;i<=3;i++){
String column ="id"+i;
sql= "select "+column+" from test";
PreparedStatement pstm= conn.prepareStatement(sql);
ResultSet rs = pstm.executeQuery();
while(rs.next()){
....
....
}
....
....
}
一般在我们Java程序中,要及时关闭释放连接资源,不然游标cursor就会爆掉,这个跟Oracle中初始化参数open_cursor有关对于上面的查询,由于查询sql是在变化的,PreparedStatement需要每次创建,然后打开一个游标,如果外层for循环很多,如果资源不及时释放,就会有问题。这就涉及到资源PreparedStatement何时关闭的问题。

在我们erp系统平台中,ubc01监控平台可以看到当前系统有哪些cursor正在执行,之前我也有测试过,通过系统组得知,ubc01中的信息来自v$open_cursor,其实是不准确的。

言归正传,回到上面的问题,资源何时关闭?
由于创建PreparedStatement的sql是动态组出来的,因此不能在创建PreparedStatement的时候先去判断是否为null,这根我们平常写法不同,也就是说不能
PreparedStatement  pstm =null;
if(pstm == null)
pstm = conn.prepareStatement(sql);

....
....
这样虽然只会开启一个cursor,执行的查询SQL不管怎么变,pstm只创建一次,并不是我的实际需求。

针对这个问题,有如下几种方法可以解决:

第一种:一开一闭
也就是说,在循环里面,每次重新创建一个PreparedStatement开启一个cursor,执行完之后马上关闭PreparedStatement
即:
for (int i=1;i<=3;i++){
String column ="id"+i;
sql= "select "+column+" from test";
PreparedStatement pstm= conn.prepareStatement(sql);
ResultSet rs = pstm.executeQuery();
while(rs.next()){
....
....
}
rs.close();
rs=null;
pstm.close();
pstm=null;
}

第二种:只创建一个PreparedStatement开启一个cursor
换个思路,这里将查询的动态sql给写死,即查询所有,这样只需要创建一次PrepareStatement,只是关键在于获取结果集
的时候,执行锁定栏位,即:
PreparedStatement pstm=null;
ResultSet rs =null;
for (int i=1;i<=3;i++){
String column ="id"+i;
sql= "select * from test";
if(pstm==null)
pstm = conn.prepareStatement(sql);
rs = pstm.executeQuery();
while(rs.next()){
String name = rs.getString(column);  --关键在这!
....
}
....
....
}
rs.close();
rs=null;
pstm.close();
pstm=null;

这样资源就可以只创建一次,然后在循环外面关闭就可以了,符合我们一般的思路。

第三种:存储过程返回动态结果集
回到文章的标题,也是我真正想记录的东西。
当我们在一个存储过程中返回一个动态结果集的时候,Java端如何调用获取呢?这是我最想关心的
因为我喜欢把很多逻辑写到Oracle端得procedures中,想得到什么结果,直接在procedures中通过refcursor参数返回即可。

针对上面的资源释放问题,我同样可以换个思路,将动态获取的查询放到Oracle的procedure中,Java端只需调用一次我的procedure这样资源的开启释放都和我们一般写法一样的
首先写我想要的procedure:
create or replace procedure p (cur out sys_refcursor,colname varchar2)
is
sql_str varchar2(3000);
begin
sql_str:='select '||colname||' from test';
open cur for sql_str;
end;
然后在Java端调用这个过程:

CallableStatement cs =null;
ResultSet rs =null;
for (int i=1;i<=3;i++){
String column ="id"+i;
if(cs==null)
cs=conn.prepareCall("{call p(?,?)}");
cs.registerOutParameter(1, OracleTypes.CURSOR);
cs.setString(2,column);
cs.execute();
rs=(ResultSet)cs.getObject(1);
while(rs.next()){
System.out.println(rs.getString(1));
System.out.println(rs.getString(column));

}
....
....
}
rs.close();
rs=null;
cs.close();
cs=null;

综上所述,对于上面三种方法,最好的是第二种,因为从始至终只会开启一个cursor!
第一种虽然还是节省资源,但每次都会开启然后又关闭又重新开启,比较麻烦。
最差的是第三种!
为什么这么说呢?我通过调试发现,Java端prepareCall第一次会开启一个plsql块的cursor :begin p(?,?); end;
然后每次cs.execute()得时候,在过程中会每次开启一个cursor: select id1/id2/id3 from test;
也就是说,每次cs.execute()执行完后,未及时将open cursor关闭!
通过程序可以看到,procedure中开启了动态refcursor:open cur for ....
这样该游标是开启了,然而在Java端接收的游标 OracleTypes.CURSOR并没有在循环中每次调用procedure完就关闭因此cursor数在一次次增加。
看上去我们在Java端将CallableStatement和ResultSet关闭了,但实质上procedure开启的cursor我们未关闭!

现修改第三种方法:
我们采用JDBC提供的Java和Oracle连接的另外一个接口OracleCallableStatement来创建Statement调用Oracle的procedure然后直接获取OracleType.CURSOR这样我们就可以在循环中colse cursor了
具体这样做:
OracleCallableStatement ocs = null;
for (int i=1;i<=3;i++){
String column ="id"+i;
if(ocs==null)
ocs=(OracleCallableStatement) m_Conn.prepareCall("{call p(?,?)}");
ocs.registerOutParameter(1, OracleTypes.CURSOR);
ocs.setString(2,column);
ocs.execute();
ResultSet cur = ocs.getCursor(1);
while(cur.next()){
System.out.println(column+"="+cur.getString(1));
}
cur.close();
cur=null;
....
....
}
ocs.close();
ocs=null;

这样,程序中最多只会开启两个cursor:
(1).begin p(?,?); end;  最后关闭
和
(2).select id1/id2/id3 from test; 这个每次开了每次都关闭了

其实,第三种方法直接在循环里关闭rs就够了,即:
if(cs==null)
cs=m_Conn.prepareCall("{call p(?,?)}");
cs.registerOutParameter(1, oracle.jdbc.OracleTypes.CURSOR);
cs.setString(2,column);
cs.execute();
rs=(ResultSet)cs.getObject(1);
while(rs.next()){
System.out.println(column+"="+rs.getString(1));
}
rs.close();
rs=null;

问题到此已结束!
最后附上全部Java代码:

package com.fpg.p.test;

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

import oracle.jdbc.OracleCallableStatement;
import oracle.jdbc.OracleTypes;

public class Test2 {
public static void main(String[] args) {
Connection m_Conn = null;
PreparedStatement pstm=null;
ResultSet rs = null;
CallableStatement cs =null;
OracleCallableStatement ocs = null;
try {
//1.构造驱动
Class.forName("oracle.jdbc.driver.OracleDriver");
// 2.创建连接
m_Conn = DriverManager.getConnection(
"jdbc:oracle:thin:@***.***.***.***:1521:****", "sys as sysdba", "****");
String column=null;
String sql = null;

for(int i=1;i<=3;i++){
column="id"+i;
//				1
//				sql = "select "+column+" from test";
//				pstm = m_Conn.prepareStatement(sql);
//				rs = pstm.executeQuery();
//				while(rs.next()){
//					System.out.println(rs.getString(column));
//				}
//				rs.close();
//				rs=null;
//				pstm.close();
//				pstm=null;

//2
//				sql = "select * from test";
//				if (pstm==null)
//				pstm = m_Conn.prepareStatement(sql);
//				rs = pstm.executeQuery();
//				while(rs.next()){
//					System.out.println(rs.getString(column));
//				}

//			    3
//				if(cs==null)
//				cs=m_Conn.prepareCall("{call p(?,?)}");
//				cs.registerOutParameter(1, oracle.jdbc.OracleTypes.CURSOR);
//				cs.setString(2,column);
//				cs.execute();
//				rs=(ResultSet)cs.getObject(1);
//				while(rs.next()){
//					System.out.println(column+"="+rs.getString(1));
//				}
//                              rs.close();
//                              rs=null;

//				 4
if(ocs==null)
ocs=(OracleCallableStatement) m_Conn.prepareCall("{call p(?,?)}");
ocs.registerOutParameter(1, OracleTypes.CURSOR);
ocs.setString(2,column);
ocs.execute();
ResultSet cur = ocs.getCursor(1);
while (cur.next()){
System.out.println(column+"="+cur.getString(1));
}
cur.close();
cur=null;

}

} catch (Exception e) {
e.printStackTrace();
}finally{
try {
if(pstm!=null){
pstm.close();
pstm=null;
}
if(rs!=null){
rs.close();
rs=null;
}
if(cs!=null){
cs.close();
cs=null;
}
if(ocs!=null){
ocs.close();
ocs=null;
}
if(m_Conn!=null){
m_Conn.close();
m_Conn=null;
}
} catch (Exception e1) {
//
e1.printStackTrace();
}
}

}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: