java 调用存储过程 如何获得返回值?

时间:2022-05-08 15:46:02
Java批量出入数据回滚处理,部分代码如下:
int[] count2 = {1,2};
ConDB conDB = new ConDB();
BillTop billTop = sssForm.getBillTop();
List<BillDetail> billDetailList = sssForm.getBillDetailList();
String sql1 = "{?= call  YP_insertBillMasterOrder(?,?,?,?,?,?,?,?,?,?)}";
String sql2 = "{?= call  YP_insertBillDetailOrder(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)}";
Connection con = conDB.getConnection();
try {

CallableStatement cstm1 = conDB.getCstm(sql1);
CallableStatement cstm2 = conDB.getCstm(sql2);

cstm1.registerOutParameter(1, java.sql.Types.INTEGER);
cstm1.setString(2, billTop.getBillSN());
cstm1.setString(3, billTop.getInputDate());
cstm1.setString(4, billTop.getClientID());
cstm1.setString(5, billTop.getStaffID());
cstm1.setString(6, billTop.getFinishDate());
cstm1.setString(7, billTop.getStorageID1());
cstm1.setInt(8, billTop.getBillType());
cstm1.setString(9, billTop.getSummary());
cstm1.setString(10, billTop.getMemo());
cstm1.setString(11, loginerID);
cstm1.execute();
billID = cstm1.getInt(1);
System.out.println(billID);
con.setAutoCommit(false);
for(int n=0;n<billDetailList.size()-1;n++){
BillDetail billDetail = billDetailList.get(n);
cstm2.registerOutParameter(1, java.sql.Types.INTEGER);
cstm2.setInt(2, billID);
cstm2.setString(3,billDetail.getAccountID());
cstm2.setString(4,billTop.getInputDate());
cstm2.setString(5,billTop.getClientID() );
cstm2.setString(6,billTop.getStaffID() );
cstm2.setString(7,billTop.getStorageID1() );
cstm2.setString(8,billTop.getStorageID2() );
cstm2.setString(9,billDetail.getWareID() );
cstm2.setString(10,billDetail.getProducedDate());
cstm2.setString(11,billDetail.getBatchNo());
cstm2.setString(12,billDetail.getCostMode());
cstm2.setInt(13,billDetail.getUnit());

cstm2.setDouble(14,billDetail.getAmount() );
cstm2.setDouble(15, billDetail.getToAmount());
cstm2.setDouble(16, billDetail.getPrice());
cstm2.setDouble(17, billDetail.getCostPrice());
cstm2.setDouble(18, billDetail.getSumTo());
cstm2.setDouble(19, billDetail.getCostSumTo());
cstm2.setDouble(20, billDetail.getDiscount());
cstm2.setDouble(21, billDetail.getDiscountPrice());
cstm2.setDouble(22, billDetail.getDiscountSumTo());
cstm2.setDouble(23, billDetail.getTax());
cstm2.setDouble(24, billDetail.getTaxAtion());
cstm2.setDouble(25, billDetail.getTaxPrice());
cstm2.setDouble(26, billDetail.getTaxSumTo());
cstm2.setInt(27,billTop.getBillType());
cstm2.setString(28, billDetail.getOrientation());
cstm2.setDouble(29, billTop.getPeriod());
cstm2.setString(30, billDetail.getRemark());
count2[n] = cstm2.getInt(1);
cstm2.addBatch();
//count2[n] = cstm2.getInt(1);
}

cstm2.executeBatch();
con.commit();

for(int n=0;n<count2.length;n++){
System.out.println("count_"+count2[n]);
if(count2[n]==0)
suc = false;
}
如果我把获取返回语句count2[n] = cstm2.getInt(1)去掉,则代码可以正常运行。
不去掉就会报下面的错误,上面两个位置都会报错:

java.sql.SQLException: [Microsoft][SQLServer 2000 Driver for JDBC]The requested data is not available.
at com.microsoft.jdbc.base.BaseExceptions.createException(Unknown Source)
at com.microsoft.jdbc.base.BaseExceptions.getException(Unknown Source)
at com.microsoft.jdbc.base.BaseCallableStatement.getAndValidateOutParameter(Unknown Source)
at com.microsoft.jdbc.base.BaseCallableStatement.getInt(Unknown Source)
at net.yjiasoft.sss.dao.sss.InsertBillDAO.insertBillOrder(InsertBillDAO.java:78)
at net.yjiasoft.sss.action.sss.StorageSellAction.saveOrderBill(StorageSellAction.java:52)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.apache.struts.actions.DispatchAction.dispatchMethod(DispatchAction.java:274)
at org.apache.struts.actions.DispatchAction.execute(DispatchAction.java:194)
at org.apache.struts.action.RequestProcessor.processActionPerform(RequestProcessor.java:419)
at org.apache.struts.action.RequestProcessor.process(RequestProcessor.java:224)
at org.apache.struts.action.ActionServlet.process(ActionServlet.java:1194)
at org.apache.struts.action.ActionServlet.doPost(ActionServlet.java:432)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:710)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:803)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:269)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:188)
at net.yjiasoft.sss.filter.SetCharacterEncodingFilter.doFilter(SetCharacterEncodingFilter.java:60)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:215)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:188)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:213)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:174)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:117)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:108)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:151)
at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:874)
at org.apache.coyote.http11.Http11BaseProtocol$Http11ConnectionHandler.processConnection(Http11BaseProtocol.java:665)
at org.apache.tomcat.util.net.PoolTcpEndpoint.processSocket(PoolTcpEndpoint.java:528)
at org.apache.tomcat.util.net.LeaderFollowerWorkerThread.runIt(LeaderFollowerWorkerThread.java:81)
at org.apache.tomcat.util.threads.ThreadPool$ControlRunnable.run(ThreadPool.java:689)
at java.lang.Thread.run(Thread.java:619)

8 个解决方案

#1


我要返回的是存储过程里面的返回值,如return 5455,就得到5455
而不是影响的记录条数!!!

#2


复杂的返回值     
    
  关于存储过程的知识,很多人好像就熟悉我们所讨论的这些。如果这是存储过程的全部功能,那么存储过程就不是其它远程执行机制的替换方案了。存储过程的功能比这强大得多。     
  当你执行一个SQL查询时,DBMS创建一个叫做cursor(游标)的数据库对象,用于在返回结果中迭代每一行。ResultSet是当前时间点的游标的一个表示。这就是为什么没有缓存或者特定数据库的支持,你只能在ResultSet中向前移动。     
  某些DBMS允许从存储过程中返回游标的一个引用。JDBC并不支持这个功能,但是Oracle、PostgreSQL和DB2的JDBC驱动器都支持在ResultSet上打开到游标的指针(pointer)。     
  设想列出所有没有活到退休年龄的诗人,下面是完成这个功能的存储过程,返回一个打开的游标,同样也使用PostgreSQL的pl/pgsql语言:     
    
  create   procedure   list_early_deaths   ()   return   refcursor   as   'declare     
    
          toesup   refcursor;   
    
  begin     
    
          open   toesup   for   SELECT   poets.name,   deaths.age   FROM   poets,   deaths   --   all   entries   in   deaths   are   for   poets.   --   but   the   table   might   become   generic.     
    
          WHERE   poets.id   =   deaths.mort_id   AND   deaths.age   <   60;     
    
          return   toesup;   
    
  end;'   language   'plpgsql';     
    
  下面是调用该存储过程的Java方法,将结果输出到PrintWriter:     
  PrintWriter:     
    
  static   void   sendEarlyDeaths(PrintWriter   out){     
    
          Connection   con   =   null;   
    
          CallableStatement   toesUp   =   null;   
    
          try   {   
    
                  con   =   ConnectionPool.getConnection();     
    
                    //   PostgreSQL   needs   a   transaction   to   do   this...   con.   
    
                  setAutoCommit(false);   //   Setup   the   call.     
    
                  CallableStatement   toesUp   =   connection.prepareCall("{   ?   =   call   list_early_deaths   ()   }");     
    
                    toesUp.registerOutParameter(1,   Types.OTHER);     
    
                    toesUp.execute();     
    
                    ResultSet   rs   =   (ResultSet)   toesUp.getObject(1);     
    
                    while   (rs.next())   {   
    
                                  String   name   =   rs.getString(1);   
    
                                  int   age   =   rs.getInt(2);     
    
                                  out.println(name   +   "   was   "   +   age   +   "   years   old.");     
    
                      }     
    
                      rs.close();     
    
                }       
    
            catch   (SQLException   e)   {   //   We   should   protect   these   calls.   toesUp.close();   con.close();   
    
          }   
    
  }     
    
  因为JDBC并不直接支持从存储过程中返回游标,我们使用Types.OTHER来指示存储过程的返回类型,然后调用getObject()方法并对返回值进行强制类型转换。     
  这个调用存储过程的Java方法是mapping的一个好例子。Mapping是对一个集上的操作进行抽象的方法。不是在这个过程上返回一个集,我们可以把操作传送进去执行。本例中,操作就是把ResultSet打印到一个输出流。这是一个值得举例的很常用的例子,下面是调用同一个存储过程的另外一个方法实现:     
    
  public   class   ProcessPoetDeaths{     
    
          public   abstract   void   sendDeath(String   name,   int   age);   
    
  }   
    
          static   void   mapEarlyDeaths(ProcessPoetDeaths   mapper){     
    
          Connection   con   =   null;     
    
          CallableStatement   toesUp   =   null;     
    
          try   {   
    
                    con   =   ConnectionPool.getConnection();     
    
                    con.setAutoCommit(false);     
    
                    CallableStatement   toesUp   =   connection.prepareCall("{   ?   =   call   list_early_deaths   ()   }");   
    
                      toesUp.registerOutParameter(1,   Types.OTHER);     
    
                      toesUp.execute();     
    
                      ResultSet   rs   =   (ResultSet)   toesUp.getObject(1);     
    
                    while   (rs.next())   {     
    
                    String   name   =   rs.getString(1);     
    
                    int   age   =   rs.getInt(2);     
    
                  mapper.sendDeath(name,   age);     
    
  }     
    
  rs.close();     
    
  }   catch   (SQLException   e)   {   //   We   should   protect   these   calls.   toesUp.close();     
    
  con.close();     
    
  }   
    
  }     
    
  这允许在ResultSet数据上执行任意的处理,而不需要改变或者复制获取ResultSet的方法:     
    
  static   void   sendEarlyDeaths(final   PrintWriter   out){     
    
                      ProcessPoetDeaths   myMapper   =   new   ProcessPoetDeaths()   {     
    
                                                              public   void   sendDeath(String   name,   int   age)   {     
    
                                                                                                      out.println(name   +   "   was   "   +   age   +   "   years   old.");     
    
                                                            }     
    
                    };     
    
  mapEarlyDeaths(myMapper);   
    
  }     
    
  这个方法使用ProcessPoetDeaths的一个匿名实例调用mapEarlyDeaths。该实例拥有sendDeath方法的一个实现,和我们上面的例子一样的方式把结果写入到输出流。当然,这个技巧并不是存储过程特有的,但是和存储过程中返回的ResultSet结合使用,是一个非常强大的工具。 

#3


引用 2 楼 javagxc 的回复:
如果我把获取返回语句count2[n] = cstm2.getInt(1)去掉,则代码可以正常运行。

肯定会出错呀,首先count2定义数组的长度是2
当你循环的时候billDetailList.size()-1的大小超过2以后肯定就会抛出异常了。

#4


感谢2楼和3楼的回答。

2楼所说的知识很深奥,鄙人还无法全理解,但是还是很感谢。
3楼,回答也是正确的,但是请注意关键性问题。

期待答案,或者解决方案。。

#5


其实用你那种方法没必要,有点麻烦

调用sqlserver 存储过程。直接拿rs就行,和执行sql语句一样,不用call 
例如: 
String sql = "select * from user"; 
Connection conn = DatabaseConnection.getConnection(); // 获得数据库连接 
stmt  = conn.prepareStatement(sql); 
rs = stmt.executeQuery(); 


以上没问题吧,如果是调用存储过程 
String sql = "spWWWClear 200812"; 
Connection conn = DatabaseConnection.getConnection(); // 获得数据库连接 
stmt  = conn.prepareStatement(sql); 
rs = stmt.executeQuery(); 

这样就OK了  


http://topic.csdn.net/u/20090708/15/9d9c00c9-4868-4eb5-83f3-7baa80bdf85c.html

#6


引用 5 楼 javagxc 的回复:
其实用你那种方法没必要,有点麻烦 

调用sqlserver 存储过程。直接拿rs就行,和执行sql语句一样,不用call 
例如: 
String sql = "select * from user"; 
Connection conn = DatabaseConnection.getConnection(); // 获得数据库连接 
stmt  = conn.prepareStatement(sql); 
rs = stmt.executeQuery(); 


以上没问题吧,如果是调用存储过程 
String sql = "spWWWClear 200812"; 
Connection conn = DatabaseConnec…

这个是我发的吧,sqlserver可以这样用,其他的数据库我就不保证能够成功了;

#7


散分了。。。。。

#8


学习一下。。

#1


我要返回的是存储过程里面的返回值,如return 5455,就得到5455
而不是影响的记录条数!!!

#2


复杂的返回值     
    
  关于存储过程的知识,很多人好像就熟悉我们所讨论的这些。如果这是存储过程的全部功能,那么存储过程就不是其它远程执行机制的替换方案了。存储过程的功能比这强大得多。     
  当你执行一个SQL查询时,DBMS创建一个叫做cursor(游标)的数据库对象,用于在返回结果中迭代每一行。ResultSet是当前时间点的游标的一个表示。这就是为什么没有缓存或者特定数据库的支持,你只能在ResultSet中向前移动。     
  某些DBMS允许从存储过程中返回游标的一个引用。JDBC并不支持这个功能,但是Oracle、PostgreSQL和DB2的JDBC驱动器都支持在ResultSet上打开到游标的指针(pointer)。     
  设想列出所有没有活到退休年龄的诗人,下面是完成这个功能的存储过程,返回一个打开的游标,同样也使用PostgreSQL的pl/pgsql语言:     
    
  create   procedure   list_early_deaths   ()   return   refcursor   as   'declare     
    
          toesup   refcursor;   
    
  begin     
    
          open   toesup   for   SELECT   poets.name,   deaths.age   FROM   poets,   deaths   --   all   entries   in   deaths   are   for   poets.   --   but   the   table   might   become   generic.     
    
          WHERE   poets.id   =   deaths.mort_id   AND   deaths.age   <   60;     
    
          return   toesup;   
    
  end;'   language   'plpgsql';     
    
  下面是调用该存储过程的Java方法,将结果输出到PrintWriter:     
  PrintWriter:     
    
  static   void   sendEarlyDeaths(PrintWriter   out){     
    
          Connection   con   =   null;   
    
          CallableStatement   toesUp   =   null;   
    
          try   {   
    
                  con   =   ConnectionPool.getConnection();     
    
                    //   PostgreSQL   needs   a   transaction   to   do   this...   con.   
    
                  setAutoCommit(false);   //   Setup   the   call.     
    
                  CallableStatement   toesUp   =   connection.prepareCall("{   ?   =   call   list_early_deaths   ()   }");     
    
                    toesUp.registerOutParameter(1,   Types.OTHER);     
    
                    toesUp.execute();     
    
                    ResultSet   rs   =   (ResultSet)   toesUp.getObject(1);     
    
                    while   (rs.next())   {   
    
                                  String   name   =   rs.getString(1);   
    
                                  int   age   =   rs.getInt(2);     
    
                                  out.println(name   +   "   was   "   +   age   +   "   years   old.");     
    
                      }     
    
                      rs.close();     
    
                }       
    
            catch   (SQLException   e)   {   //   We   should   protect   these   calls.   toesUp.close();   con.close();   
    
          }   
    
  }     
    
  因为JDBC并不直接支持从存储过程中返回游标,我们使用Types.OTHER来指示存储过程的返回类型,然后调用getObject()方法并对返回值进行强制类型转换。     
  这个调用存储过程的Java方法是mapping的一个好例子。Mapping是对一个集上的操作进行抽象的方法。不是在这个过程上返回一个集,我们可以把操作传送进去执行。本例中,操作就是把ResultSet打印到一个输出流。这是一个值得举例的很常用的例子,下面是调用同一个存储过程的另外一个方法实现:     
    
  public   class   ProcessPoetDeaths{     
    
          public   abstract   void   sendDeath(String   name,   int   age);   
    
  }   
    
          static   void   mapEarlyDeaths(ProcessPoetDeaths   mapper){     
    
          Connection   con   =   null;     
    
          CallableStatement   toesUp   =   null;     
    
          try   {   
    
                    con   =   ConnectionPool.getConnection();     
    
                    con.setAutoCommit(false);     
    
                    CallableStatement   toesUp   =   connection.prepareCall("{   ?   =   call   list_early_deaths   ()   }");   
    
                      toesUp.registerOutParameter(1,   Types.OTHER);     
    
                      toesUp.execute();     
    
                      ResultSet   rs   =   (ResultSet)   toesUp.getObject(1);     
    
                    while   (rs.next())   {     
    
                    String   name   =   rs.getString(1);     
    
                    int   age   =   rs.getInt(2);     
    
                  mapper.sendDeath(name,   age);     
    
  }     
    
  rs.close();     
    
  }   catch   (SQLException   e)   {   //   We   should   protect   these   calls.   toesUp.close();     
    
  con.close();     
    
  }   
    
  }     
    
  这允许在ResultSet数据上执行任意的处理,而不需要改变或者复制获取ResultSet的方法:     
    
  static   void   sendEarlyDeaths(final   PrintWriter   out){     
    
                      ProcessPoetDeaths   myMapper   =   new   ProcessPoetDeaths()   {     
    
                                                              public   void   sendDeath(String   name,   int   age)   {     
    
                                                                                                      out.println(name   +   "   was   "   +   age   +   "   years   old.");     
    
                                                            }     
    
                    };     
    
  mapEarlyDeaths(myMapper);   
    
  }     
    
  这个方法使用ProcessPoetDeaths的一个匿名实例调用mapEarlyDeaths。该实例拥有sendDeath方法的一个实现,和我们上面的例子一样的方式把结果写入到输出流。当然,这个技巧并不是存储过程特有的,但是和存储过程中返回的ResultSet结合使用,是一个非常强大的工具。 

#3


引用 2 楼 javagxc 的回复:
如果我把获取返回语句count2[n] = cstm2.getInt(1)去掉,则代码可以正常运行。

肯定会出错呀,首先count2定义数组的长度是2
当你循环的时候billDetailList.size()-1的大小超过2以后肯定就会抛出异常了。

#4


感谢2楼和3楼的回答。

2楼所说的知识很深奥,鄙人还无法全理解,但是还是很感谢。
3楼,回答也是正确的,但是请注意关键性问题。

期待答案,或者解决方案。。

#5


其实用你那种方法没必要,有点麻烦

调用sqlserver 存储过程。直接拿rs就行,和执行sql语句一样,不用call 
例如: 
String sql = "select * from user"; 
Connection conn = DatabaseConnection.getConnection(); // 获得数据库连接 
stmt  = conn.prepareStatement(sql); 
rs = stmt.executeQuery(); 


以上没问题吧,如果是调用存储过程 
String sql = "spWWWClear 200812"; 
Connection conn = DatabaseConnection.getConnection(); // 获得数据库连接 
stmt  = conn.prepareStatement(sql); 
rs = stmt.executeQuery(); 

这样就OK了  


http://topic.csdn.net/u/20090708/15/9d9c00c9-4868-4eb5-83f3-7baa80bdf85c.html

#6


引用 5 楼 javagxc 的回复:
其实用你那种方法没必要,有点麻烦 

调用sqlserver 存储过程。直接拿rs就行,和执行sql语句一样,不用call 
例如: 
String sql = "select * from user"; 
Connection conn = DatabaseConnection.getConnection(); // 获得数据库连接 
stmt  = conn.prepareStatement(sql); 
rs = stmt.executeQuery(); 


以上没问题吧,如果是调用存储过程 
String sql = "spWWWClear 200812"; 
Connection conn = DatabaseConnec…

这个是我发的吧,sqlserver可以这样用,其他的数据库我就不保证能够成功了;

#7


散分了。。。。。

#8


学习一下。。