极富挑战性的问题,java如何调用带输出参数,而且有结果集返回的 MSSQLSERVER存储过程

时间:2021-01-21 00:44:58
有个 mssql server 存储过程, 返回一个记录集, 同时还有输出参数输出一些额外信息

请问java该如何使用 CallableStatement 接口来完成存储过程的调用, 要能得到记录集, 还有输出参数值.  郁闷啊.

我在实践中确实得到了 ResultSet , 但是, 当调用得到的 ResultSet 的 next() 方法时就报这个错误:
[Microsoft][SQLServer 2000 Driver for JDBC]Object has been closed


看了一些网上的有关oracle文章,通过将结果集注册 (registerOutParameter)为 OracleTypes.CURSOR 类型,能得以实现, 还有的将结果集注册为 java.sql.Types.OTHER 也可以. 最后通过 (ResultSet)CallableStatement.getObject(1) 得到ResultSet . 但我在 mssql server 下测试时, mssql server 的 jdbc driver 总是说不支持这样的数据类型.

狂郁闷. 谁有这方面经验能给俺分享分享吗 小弟先在此谢过大家了

8 个解决方案

#1


返回CURSOR类型的,我见过有人用以下方法
ResultSet rs = null; 
int updateCount = -1; 
flag = cs.execute(); 
do{ 
    updateCount = cs.getUpdateCount(); 
    if(updateCount != -1){//说明当前行是一个更新计数 
        //处理. 
        cs.getMoreResults(); 
        continue;//已经是更新计数了,处理完成后应该移动到下一行 
             //不再判断是否是ResultSet 
    } 
    rs = cs.getResultSet(); 
    if(rs != null){//如果到了这里,说明updateCount == -1 
        //处理rs 
        cs.getMoreResults(); 
        continue; 
            //是结果集,处理完成后应该移动到下一行 
    } 
    //如果到了这里,说明updateCount == -1 && rs == null,什么也没的了 
    
}while(!(updateCount == -1 && rs == null)); 
------------------------------------------------
我是一直用oracler的,用(ResultSet)CallableStatement.getObject(1)没问题
手头没用SQLSERVER,上面程序供你参考

#2


试下, 多谢楼上兄弟

#3


还是不行, 明明拿到了 ResultSet , 就是说这个错 ojbect has been closed 
而且通过stmt.getMetaData 方法, 查看到 ResultSet 的metadata 也确是我想要的结果. 为什么就不给我呢????

#4



声明
private CallableStatement sqlSend;
sqlSend = remoteConn.prepareCall("{ call " + dataUser + ".proGroupsms1(?,?,?,?,?,?,?,?,?) }");

执行
try{
sqlSend.setString(1,sendSms.sp_num);
sqlSend.setString(2,sendSms.dst_num);
sqlSend.setString(3,sendSms.busi_code);
sqlSend.setByte(4,sendSms.msg_code);
sqlSend.setString(5,sendSms.content);
sqlSend.setString(6,sendSms.reserve);
sqlSend.registerOutParameter(7,java.sql.Types.INTEGER);
sqlSend.setString(8,NewClient.spNum);
sqlSend.setString(9,sendSms.msgid);
sqlSend.executeUpdate();
remoteConn.commit();

errSms = new ErrorSMS();
errSms.msg_id = sendSms.msg_id;
errSms.err_code = (byte)sqlSend.getInt(7);
if (NewClient.in_debug == 1)
System.out.println("[RemoteDBThread]Put a RetCode:" + errSms.err_code);
errSms = (ErrorSMS)merrQueue.writeNoWait(errSms);

sendSms = (SendSMS)msendQueue.read(sendSms);
}catch(Exception e){
e.printStackTrace();
System.out.println("[RemoteDBThread]发送短信过程出错,跳过......");
remoteConn.rollback();
sendSms = (SendSMS)msendQueue.read(sendSms);
}

#5


声明
private CallableStatement sqlSend;
sqlSend = remoteConn.prepareCall("{ call " + dataUser + ".proGroupsms1(?,?,?,?,?,?,?,?,?) }");

执行
try{
    sqlSend.setString(1,sendSms.sp_num);
    sqlSend.setString(2,sendSms.dst_num);
    sqlSend.setString(3,sendSms.busi_code);
    sqlSend.setByte(4,sendSms.msg_code);
    sqlSend.setString(5,sendSms.content);
    sqlSend.setString(6,sendSms.reserve);
    sqlSend.registerOutParameter(7,java.sql.Types.INTEGER);
    sqlSend.setString(8,NewClient.spNum);
    sqlSend.setString(9,sendSms.msgid);
    sqlSend.executeUpdate();
    remoteConn.commit();
    errSms = new ErrorSMS();
    errSms.msg_id = sendSms.msg_id;
    errSms.err_code = (byte)sqlSend.getInt(7);
    if (NewClient.in_debug == 1)
    System.out.println("[RemoteDBThread]Put a RetCode:" + errSms.err_code);
    errSms = (ErrorSMS)merrQueue.writeNoWait(errSms);
    sendSms = (SendSMS)msendQueue.read(sendSms);
 }catch(Exception e){
    e.printStackTrace();
    System.out.println("[RemoteDBThread]发送短信过程出错,跳过......");
    remoteConn.rollback();
    sendSms = (SendSMS)msendQueue.read(sendSms);
 }
这是JAVA调用ORACLE存储过程非常好用安全的程序段,不知道是不是有用!

#6


我试了一下,上面的程序是可用的,只是有个小问题
以下是我的测试程序:
public class sqltest {

/**
 * @param args
 */
public static void main(String[] args) {
// TODO Auto-generated method stub
try{
Connection conn;
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
conn=DriverManager.getConnection("jdbc:odbc:Driver={SQL Server};Server=vpc98;uid=sa;pwd=;Database=master");
String sql="";
Statement st=conn.createStatement();
ResultSet rs;
/* sql="select * from test";
rs=st.executeQuery(sql);
while (rs.next())
{
String id=rs.getString("postcode");
System.out.println(id);
}
if (rs!=null) rs.close();
*/
CallableStatement cs = conn.prepareCall("dbo.sp_tables");
rs = null; 
int updateCount = -1; 
boolean flag = cs.execute(); 
do{ 
    updateCount = cs.getUpdateCount(); 
    if(updateCount != -1){//说明当前行是一个更新计数 
        //处理. 
        cs.getMoreResults(); 
        System.out.println(updateCount);
        continue;//已经是更新计数了,处理完成后应该移动到下一行 
             //不再判断是否是ResultSet 
    } 
    rs = cs.getResultSet(); 
    if(rs != null){//如果到了这里,说明updateCount == -1 
        //处理rs 
     System.out.println("ok");
     while (rs.next())
{
   String id=rs.getString("table_name");
   System.out.println(id);
}
     rs.close();
     rs = null;
        //cs.getMoreResults(); 
        continue; 
            //是结果集,处理完成后应该移动到下一行 
    } 
    //如果到了这里,说明updateCount == -1 && rs == null,什么也没的了 
    
}while(!(updateCount == -1 && rs == null)); 

if (st!=null)st.close();
if (conn!=null) conn.close();
}
catch(Exception e){
System.out.println(e.toString());
}

}

}
我是以系统的sp_tables过程作为测试的,它的返回就是一个记录集
不过我不知如果是在参数中返回是否可以(sp_tables是在结果中返回的),因为我对SQL Server不熟
楼主可再试一下你的存储过程是否可用
另外,楼上的是用oracle,我上面提过oracle用(ResultSet)CallableStatement.getObject(1)是肯定可以的,我就这样用过

#7


谢谢楼上热心的朋友.
我已经找到问题的答案了.
如果存储过程返回结果集, 并且同时还有输出参数的话, 在java里面应该使用完结果集, 再使用getXXX(parameterIndex)这样的方法获取输出参数, 因为每当使用getXXX系列方法获取输出参数时,jdbc会自动关闭结果集......残念呀......在jdk文档里关于CallableStatement接口的说明有这样一段话,再明白不过了:For maximum portability, a call's ResultSet objects and update counts should be processed prior to getting the values of output parameters. 


谢谢上面热心参与的朋友,本贴保留至今晚(2005-09-22),到时将结贴散分.

#8


学习

#1


返回CURSOR类型的,我见过有人用以下方法
ResultSet rs = null; 
int updateCount = -1; 
flag = cs.execute(); 
do{ 
    updateCount = cs.getUpdateCount(); 
    if(updateCount != -1){//说明当前行是一个更新计数 
        //处理. 
        cs.getMoreResults(); 
        continue;//已经是更新计数了,处理完成后应该移动到下一行 
             //不再判断是否是ResultSet 
    } 
    rs = cs.getResultSet(); 
    if(rs != null){//如果到了这里,说明updateCount == -1 
        //处理rs 
        cs.getMoreResults(); 
        continue; 
            //是结果集,处理完成后应该移动到下一行 
    } 
    //如果到了这里,说明updateCount == -1 && rs == null,什么也没的了 
    
}while(!(updateCount == -1 && rs == null)); 
------------------------------------------------
我是一直用oracler的,用(ResultSet)CallableStatement.getObject(1)没问题
手头没用SQLSERVER,上面程序供你参考

#2


试下, 多谢楼上兄弟

#3


还是不行, 明明拿到了 ResultSet , 就是说这个错 ojbect has been closed 
而且通过stmt.getMetaData 方法, 查看到 ResultSet 的metadata 也确是我想要的结果. 为什么就不给我呢????

#4



声明
private CallableStatement sqlSend;
sqlSend = remoteConn.prepareCall("{ call " + dataUser + ".proGroupsms1(?,?,?,?,?,?,?,?,?) }");

执行
try{
sqlSend.setString(1,sendSms.sp_num);
sqlSend.setString(2,sendSms.dst_num);
sqlSend.setString(3,sendSms.busi_code);
sqlSend.setByte(4,sendSms.msg_code);
sqlSend.setString(5,sendSms.content);
sqlSend.setString(6,sendSms.reserve);
sqlSend.registerOutParameter(7,java.sql.Types.INTEGER);
sqlSend.setString(8,NewClient.spNum);
sqlSend.setString(9,sendSms.msgid);
sqlSend.executeUpdate();
remoteConn.commit();

errSms = new ErrorSMS();
errSms.msg_id = sendSms.msg_id;
errSms.err_code = (byte)sqlSend.getInt(7);
if (NewClient.in_debug == 1)
System.out.println("[RemoteDBThread]Put a RetCode:" + errSms.err_code);
errSms = (ErrorSMS)merrQueue.writeNoWait(errSms);

sendSms = (SendSMS)msendQueue.read(sendSms);
}catch(Exception e){
e.printStackTrace();
System.out.println("[RemoteDBThread]发送短信过程出错,跳过......");
remoteConn.rollback();
sendSms = (SendSMS)msendQueue.read(sendSms);
}

#5


声明
private CallableStatement sqlSend;
sqlSend = remoteConn.prepareCall("{ call " + dataUser + ".proGroupsms1(?,?,?,?,?,?,?,?,?) }");

执行
try{
    sqlSend.setString(1,sendSms.sp_num);
    sqlSend.setString(2,sendSms.dst_num);
    sqlSend.setString(3,sendSms.busi_code);
    sqlSend.setByte(4,sendSms.msg_code);
    sqlSend.setString(5,sendSms.content);
    sqlSend.setString(6,sendSms.reserve);
    sqlSend.registerOutParameter(7,java.sql.Types.INTEGER);
    sqlSend.setString(8,NewClient.spNum);
    sqlSend.setString(9,sendSms.msgid);
    sqlSend.executeUpdate();
    remoteConn.commit();
    errSms = new ErrorSMS();
    errSms.msg_id = sendSms.msg_id;
    errSms.err_code = (byte)sqlSend.getInt(7);
    if (NewClient.in_debug == 1)
    System.out.println("[RemoteDBThread]Put a RetCode:" + errSms.err_code);
    errSms = (ErrorSMS)merrQueue.writeNoWait(errSms);
    sendSms = (SendSMS)msendQueue.read(sendSms);
 }catch(Exception e){
    e.printStackTrace();
    System.out.println("[RemoteDBThread]发送短信过程出错,跳过......");
    remoteConn.rollback();
    sendSms = (SendSMS)msendQueue.read(sendSms);
 }
这是JAVA调用ORACLE存储过程非常好用安全的程序段,不知道是不是有用!

#6


我试了一下,上面的程序是可用的,只是有个小问题
以下是我的测试程序:
public class sqltest {

/**
 * @param args
 */
public static void main(String[] args) {
// TODO Auto-generated method stub
try{
Connection conn;
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
conn=DriverManager.getConnection("jdbc:odbc:Driver={SQL Server};Server=vpc98;uid=sa;pwd=;Database=master");
String sql="";
Statement st=conn.createStatement();
ResultSet rs;
/* sql="select * from test";
rs=st.executeQuery(sql);
while (rs.next())
{
String id=rs.getString("postcode");
System.out.println(id);
}
if (rs!=null) rs.close();
*/
CallableStatement cs = conn.prepareCall("dbo.sp_tables");
rs = null; 
int updateCount = -1; 
boolean flag = cs.execute(); 
do{ 
    updateCount = cs.getUpdateCount(); 
    if(updateCount != -1){//说明当前行是一个更新计数 
        //处理. 
        cs.getMoreResults(); 
        System.out.println(updateCount);
        continue;//已经是更新计数了,处理完成后应该移动到下一行 
             //不再判断是否是ResultSet 
    } 
    rs = cs.getResultSet(); 
    if(rs != null){//如果到了这里,说明updateCount == -1 
        //处理rs 
     System.out.println("ok");
     while (rs.next())
{
   String id=rs.getString("table_name");
   System.out.println(id);
}
     rs.close();
     rs = null;
        //cs.getMoreResults(); 
        continue; 
            //是结果集,处理完成后应该移动到下一行 
    } 
    //如果到了这里,说明updateCount == -1 && rs == null,什么也没的了 
    
}while(!(updateCount == -1 && rs == null)); 

if (st!=null)st.close();
if (conn!=null) conn.close();
}
catch(Exception e){
System.out.println(e.toString());
}

}

}
我是以系统的sp_tables过程作为测试的,它的返回就是一个记录集
不过我不知如果是在参数中返回是否可以(sp_tables是在结果中返回的),因为我对SQL Server不熟
楼主可再试一下你的存储过程是否可用
另外,楼上的是用oracle,我上面提过oracle用(ResultSet)CallableStatement.getObject(1)是肯定可以的,我就这样用过

#7


谢谢楼上热心的朋友.
我已经找到问题的答案了.
如果存储过程返回结果集, 并且同时还有输出参数的话, 在java里面应该使用完结果集, 再使用getXXX(parameterIndex)这样的方法获取输出参数, 因为每当使用getXXX系列方法获取输出参数时,jdbc会自动关闭结果集......残念呀......在jdk文档里关于CallableStatement接口的说明有这样一段话,再明白不过了:For maximum portability, a call's ResultSet objects and update counts should be processed prior to getting the values of output parameters. 


谢谢上面热心参与的朋友,本贴保留至今晚(2005-09-22),到时将结贴散分.

#8


学习