Java开发中,使用Oracle数据库的时候,经常会碰到有ORA-01000: maximum open cursors exceeded.的错误。
实际上,这个错误的原因,主要还是代码问题引起的。
ORA-01000: maximum open cursors exceeded,表示已经达到一个进程打开的最大游标数。
这样的错误很容易出现在Java代码中的主要原因是:Java代码在执
行conn.createStatement()和conn.prepareStatement()的时候,实际上都是相当与在数据库中打开了一个
cursor。尤其是,如果你的createStatement和prepareStatement是在一个循环里面的话,就会非常容易出现这个问题。因
为游标一直在不停的打开,而且没有关闭。
一般来说,在写Java代码的时候,createStatement和prepareStatement都应该要放在循环外面,而且使用了这些
Statment后,及时关闭。最好是在执行了一次executeQuery、executeUpdate等之后,如果不需要使用结果集
(ResultSet)的数据,就马上将Statment关闭。
对于出现ORA-01000错误这种情况,单纯的加大open_cursors并不是好办法,那只是治标不治本。实际上,代码中的隐患并没有解除。
而且,绝大部分情况下,open_cursors只需要设置一个比较小的值,就足够使用了,除非有非常特别的要求。
oracle 9i 默认的open_cursors=300
一、看有问题的代码
1 import java.sql.Connection;
2 import java.sql.DriverManager;
3 import java.sql.SQLException;
4 import java.sql.Statement;
5
6 public class Test {
7 public Connection getConnection() {
8 String url = "jdbc:oracle:thin:@localhost:1521:ora9i";
9 String user = "scott";
10 String password = "tiger";
11 Connection con = null;
12 try {
13 Class.forName("oracle.jdbc.driver.OracleDriver").newInstance();
14 con = DriverManager.getConnection(url, user, password);
15 } catch (Exception e) {
16 e.printStackTrace();
17 }
18 return con;
19 }
20
21 public static void main(String[] args) throws SQLException {
22 long a = 13819100000L;
23 long b = 13819100600L; // 问题点
24 Connection con = null;
25 Statement stmt = null;
26 Test insert = new Test();
27 try {
28 con = insert.getConnection();
29 for (long c = a; c <= b; c++) {
30 String sql = "insert into telepnum values(" + c + ")";
31 stmt = con.createStatement(); // 这里是问题的所在
32 stmt.executeUpdate(sql);
33 }
34 System.out.println("OK");
35 } catch (Exception e) {
36 e.printStackTrace();
37 } finally {
38 if (con != null) {
39 con.close();
40 }
41 }
42 }
43 }
二、分析
在循环里面每次都
stmt = con.createStatement();
而没有释放,这样每个都占用了一个服务器的游标资源,最后造成失败
三、解决方案
1、增加关闭语句
1 con = insert.getConnection();
2 for (long c = a; c <= b; c++) {
3 String sql = "insert into telepnum values(" + c + ")";
4 stmt = con.createStatement(); // 这里是问题的所在
5 stmt.executeUpdate(sql);
6 stmt.close(); // 用完了就关闭好了
7 }
2、将这句话移动到循环外面,推荐用这个
1 con = insert.getConnection();
2 stmt = con.createStatement(); // 移动到这里,Statemet是可以重用的
3 for (long c = a; c <= b; c++) {
4 String sql = "insert into telepnum values(" + c + ")";
5 stmt.executeUpdate(sql);
6 }
7 stmt.close(); // 用完了就关闭好了
3、改装成批量更新
1 con = insert.getConnection();
2 con.setAutoCommit(false);
3 stmt = con.createStatement(); // 移动到这里,Statemet是可以重用的
4 for (long c = a; c <= b; c++) {
5 String sql = "insert into telepnum values(" + c + ")";
6 stmt.addBatch(sql);
7 }
8 stmt.executeBatch();
9 con.commit();
四、总结
鉴于上面的问题,在做基类的时候,在对数据进行DML操作的时候,尽量不要让基类返回Statement,而应该在基类直接进行关闭。在做查询的时候,可
以把statement留给程序员自己进行手动关闭,关闭的方法为:给ResultSet一个方法可以得到Statement,然后再关闭
Statement。个人认为这种方法是比较妥当的。