遗忘的Statement对象

最近在修改多线程批量生成静态报表相关问题时,遇到一个问题,因此回顾一下遗忘的statement对象。

问题描述:
在获取待生成的静态报表时采用连接数据库后创建了Statement对象并调用executeQuery(sql)方法返回ResultSet结果集,然后循环取出相应的记录。山寨测试代码如下:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
public class ResultSetTest {

     public static void main(String[] args) {
		
	String sql1=" select * from LKG_SR_EXCEL_TASK ";		
        Connection conn=null;
	Statement stmt=null;
	ResultSet rs=null;
	ResultSet rsProc=null;
		
    try{
    	  Class.forName("oracle.jdbc.driver.OracleDriver");
          conn=DriverManager.getConnection("jdbc:oracle:thin:@192.168.23.45:1521:ORABI","portal","portal123");
          
		  stmt=conn.createStatement();
		  rs=stmt.executeQuery(sql1);
		  int i = 0;
		  while(rs.next()){
			System.out.println("i = "+ i++);
			rsProc=stmt.executeQuery(sql1)<span style="color:#000000;">;</span>
			int j =0;
			while(rsProc.next()){
				System.out.println("j = "+ j++);
			}			
		  }
    	}catch(Exception e){
    		System.out.println("数据库连接失败!");
    	}finally{
			try{
				if(rs!=null){
					rs.close();
				}
				if(stmt!=null){
					stmt.close();
				}
				if(conn!=null){
					conn.close();
				}
			}catch(Exception e){
				System.out.println("数据库连接关闭失败!");
			}
		}
	}
}

rs结果集中应该为多条数据,然而rs.next()只能取出集合中的第一条,但是rsProc.next()去记录是正常的。

 

问题解决的方式:
1.新增一个statement对象Statement stmt2=null;
2.将rsProc=stmt.executeQuery(sql1);修改如下:
   stmt2 = conn.createStatement();
   rsProc=stmt2.executeQuery(sql1);

3.在finally中添加如下:
   if(stmt2!=null){
      stmt2.close();
   }
程序运行就正常了。

 

经思考之后,原因如下:
statement用于执行静态 SQL 语句并返回它所生成结果的对象。在默认情况下,同一时间每个 Statement 对象在只能打开一个 ResultSet 对象。因此创建SQL statement在执行sql中的流程,如果读取一个 ResultSet 对象与读取另一个交叉,则这两个对象必须是由不同的 Statement 对象生成的。如果存在某个语句的打开的当前 ResultSet 对象,则Statement 接口中的所有执行方法都会隐式关闭它。这里的ResultSet对象是默认的, ResultSet 对象不可更新,仅有一个向前移动的指针。因此,只能迭代它一次,并且只能按从第一行到最后一行的顺序进行。可以生成可滚动和/或可更新的 ResultSet 对象。以下代码片段(其中 con 为有效的 Connection 对象)演示了如何生成可滚动且不受其他更新影响的、可更新的结果集。

       Statement stmt = con.createStatement(
                                      ResultSet.TYPE_SCROLL_INSENSITIVE,
                                      ResultSet.CONCUR_UPDATABLE);
       ResultSet rs = stmt.executeQuery("SELECT a, b FROM TABLE2");

修改前的rsProc=stmt.executeQuery(sql1);中stmt并没有重新创建,因此rsProc对象地址没有发生变化。在rsProc循环取记录的之后,对象中的指示记录的指针已经指向结果集尾部,因而结果集rs再次next()的时候返回的是false,即只取出结果的第一条记录。

  

1、创建 Statement 对象

      建立了到特定数据库的连接之后,就可用该连接发送 SQL 语句。Statement 对象用 Connection 的方法 createStatement 创建,如下列代码段中所示:
 
      Connection con = DriverManager.getConnection(url, "sunny", "");
      Statement stmt = con.createStatement();
 
为了执行 Statement 对象,被发送到数据库的 SQL 语句将被作为参数提供给 Statement 的方法:
      ResultSet rs = stmt.executeQuery("SELECT a, b, c FROM Table2");

2、使用 Statement 对象执行语句

      Statement 接口提供了三种执行 SQL 语句的方法:executeQuery、executeUpdate 和 execute。使用哪一个方法由 SQL 语句所产生的内容决定。 方法 executeQuery 用于产生单个结果集的语句,例如 SELECT 语句。
      方法 executeUpdate 用于执行 INSERT、UPDATE 或 DELETE 语句以及 SQL DDL语句,例如 CREATE TABLE 和 DROP TABLE。INSERT、UPDATE 或 DELETE 语句的效果是修改表中零行或多行中的一列或多列。executeUpdate 的返回值是一个整数,指示受影响的行数(即更新计数)。对于 CREATE TABLE 或 DROP TABLE 等不操作行的语句,executeUpdate 的返回值总为零。 方法 execute 用于执行返回多个结果集、多个更新计数或二者组合的语句。执行语句的所有方法都将关闭所调用的 Statement 对象的当前打开结果集(如果存在)。这意味着在重新执行 Statement 对象之前,需要完成对当前 ResultSet 对象的处理。
     应注意,继承了 Statement 接口中所有方法的 PreparedStatement 接口都有自己的 executeQuery、executeUpdate 和 execute 方法。Statement 对象本身不包含 SQL 语句,因而必须给 Statement.execute 方法提供 SQL 语句作为参数。PreparedStatement 对象并不将 SQL 语句作为参数提供给这些方法,因为它们已经包含预编译 SQL 语句。CallableStatement 对象继承这些方法的 PreparedStatement 形式。对于这些方法的 PreparedStatement 或 CallableStatement 版本,使用查询参数将抛出 SQLException。

3、语句完成

      当连接处于自动提交模式时,其中所执行的语句在完成时将自动提交或还原。语句在已执行且所有结果返回时,即认为已完成。对于返回一个结果集的 executeQuery 方法,在检索完 ResultSet 对象的所有行时该语句完成。对于方法 executeUpdate,当它执行时语句即完成。但在少数调用方法 execute 的情况中,在检索所有结果集或它生成的更新计数之后语句才完成。
 
4、关闭statement对象

  Statement 对象将由 Java 垃圾收集程序自动关闭。而作为一种好的编程风格,应在不需要 Statement 对象时显式地关闭它们。这将立即释放 DBMS 资源,有助于避免潜在的内存问题。

 

以上若有错误请指正,谢谢!

在编程中寻找快乐,在快乐中自由编程!

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值