カーソルが解放されるタイミング - ablog の続き。
Statement、ResultSet を毎回 close しながら無限ループするプログラムと、Statement、ResultSet を close せずに無限ループするプログラムを実行してみて、前者は永久に実行され、後者はカーソルリークで例外が発生して異常終了することを検証してみた。
[結論]
Statement を close すればカーソルリークは発生しない。コネクションプーリングを使わない場合は、Connection を close するとカーソルリークは発生しないが、コネクションプーリングを使う場合は Connection を close しても、Statement を close しないとカーソルリークが発生する。たぶん。
やさしく学ぶ基礎からのJDBC P.137
java.sql.Statementをクローズすると、自動的に(連鎖的に)クローズされるようにJDBC APIの仕様で定められています。
...
昔のバージョンのOracle JDBCドライバは、java.sql.ResultSetを明示的にクローズしないと問題が発生する不具合があった。
ということで、ResultSet も close しておいたほうが良いみたい。
[検証結果]
まず、Statement、ResultSet を毎回 close しながら無限ループするプログラムを実行してみる。
$ java InfiniteLooperWithCursorClosed 001 scott ... 001 scott ^C
次に、Statement、ResultSet を close せずに無限ループするプログラムを実行してみる。
$ java InfiniteLooperWithCursorOpened 001 scott ... 001 scott Error code: 1000 SQL state: 72000 java.sql.SQLException: ORA-01000: maximum open cursors exceeded at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:112) at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:331) at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:288) at oracle.jdbc.driver.T4C8Oall.receive(T4C8Oall.java:745) at oracle.jdbc.driver.T4CStatement.doOall8(T4CStatement.java:210) at oracle.jdbc.driver.T4CStatement.executeForDescribe(T4CStatement.java:804) at oracle.jdbc.driver.OracleStatement.executeMaybeDescribe(OracleStatement.java:1049) at oracle.jdbc.driver.T4CStatement.executeMaybeDescribe(T4CStatement.java:845) at oracle.jdbc.driver.OracleStatement.doExecuteWithTimeout(OracleStatement.java:1154) at oracle.jdbc.driver.OracleStatement.executeQuery(OracleStatement.java:1313) at InfiniteLooperWithCursorOpened.main(InfiniteLooperWithCursorOpened.java:18)
想定通りの結果。
追加で、Statement だけ close した場合、 ReslutSet のみ close した場合にそれぞれどうなるか検証してみた。
ResultSet をclose せずに Statement だけ close しても、カーソルリークしない。
$ java InfiniteLooperWithStmtClosed 001 scott ... 001 scott ^C
Statement をclose せずに ResultSet だけ close すると、カーソルリークした。
$ java InfiniteLooperWithRsClosed 001 scott ... 001 scott Error code: 1000 SQL state: 72000 java.sql.SQLException: ORA-01000: maximum open cursors exceeded at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:112) at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:331) at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:288) at oracle.jdbc.driver.T4C8Oall.receive(T4C8Oall.java:745) at oracle.jdbc.driver.T4CStatement.doOall8(T4CStatement.java:210) at oracle.jdbc.driver.T4CStatement.executeForDescribe(T4CStatement.java:804) at oracle.jdbc.driver.OracleStatement.executeMaybeDescribe(OracleStatement.java:1049) at oracle.jdbc.driver.T4CStatement.executeMaybeDescribe(T4CStatement.java:845) at oracle.jdbc.driver.OracleStatement.doExecuteWithTimeout(OracleStatement.java:1154) at oracle.jdbc.driver.OracleStatement.executeQuery(OracleStatement.java:1313) at InfiniteLooperWithRsClosed.main(InfiniteLooperWithRsClosed.java:18) $ java InfiniteLooperWithRsClosed
[検証用プログラムのソースコード]
- InfiniteLooperWithCursorClosed.java
import java.sql.Connection; import java.sql.DriverManager; import java.sql.Statement; import java.sql.ResultSet; import java.sql.SQLException; public class InfiniteLooperWithCursorClosed { public static void main(String args[]) { Connection conn = null; Statement stmt = null; ResultSet resultSet = null; try { Class.forName ("oracle.jdbc.driver.OracleDriver"); conn = DriverManager.getConnection ("jdbc:oracle:thin:@192.168.45.101:1521:orcl","scott","tiger"); for(;;) { stmt = conn.createStatement(); resultSet = stmt.executeQuery("select id, name from emp"); for(;resultSet.next();) { System.out.println(resultSet.getString(1) + " " + resultSet.getString(2)); } try { if (resultSet != null) { resultSet.close(); } } catch (SQLException e){ e.printStackTrace(); } try { if (stmt != null) { stmt.close(); } } catch (SQLException e){ e.printStackTrace(); } } } catch (SQLException e) { System.out.println("Error code: " + e.getErrorCode()); System.out.println("SQL state: " + e.getSQLState()); e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } finally { try { if (resultSet != null) { resultSet.close(); } } catch (SQLException e){ e.printStackTrace(); } try { if (stmt != null) { stmt.close(); } } catch (SQLException e){ e.printStackTrace(); } try { if (conn != null) { conn.close(); } } catch (SQLException e){ e.printStackTrace(); } } } }
- InfiniteLooperWithCursorOpened.java
import java.sql.Connection; import java.sql.DriverManager; import java.sql.Statement; import java.sql.ResultSet; import java.sql.SQLException; public class InfiniteLooperWithCursorOpened { public static void main(String args[]) { Connection conn = null; Statement stmt = null; ResultSet resultSet = null; try { Class.forName ("oracle.jdbc.driver.OracleDriver"); conn = DriverManager.getConnection ("jdbc:oracle:thin:@192.168.45.101:1521:orcl","scott","tiger"); for(;;) { stmt = conn.createStatement(); resultSet = stmt.executeQuery("select id, name from emp"); for(;resultSet.next();) { System.out.println(resultSet.getString(1) + " " + resultSet.getString(2)); } } } catch (SQLException e) { System.out.println("Error code: " + e.getErrorCode()); System.out.println("SQL state: " + e.getSQLState()); e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } finally { try { if (resultSet != null) { resultSet.close(); } } catch (SQLException e){ e.printStackTrace(); } try { if (stmt != null) { stmt.close(); } } catch (SQLException e){ e.printStackTrace(); } try { if (conn != null) { conn.close(); } } catch (SQLException e){ e.printStackTrace(); } } } }
- InfiniteLooperWithStmtClosed.java
import java.sql.Connection; import java.sql.DriverManager; import java.sql.Statement; import java.sql.ResultSet; import java.sql.SQLException; public class InfiniteLooperWithStmtClosed { public static void main(String args[]) { Connection conn = null; Statement stmt = null; ResultSet resultSet = null; try { Class.forName ("oracle.jdbc.driver.OracleDriver"); conn = DriverManager.getConnection ("jdbc:oracle:thin:@192.168.45.101:1521:orcl","scott","tiger"); for(;;) { stmt = conn.createStatement(); resultSet = stmt.executeQuery("select id, name from emp"); for(;resultSet.next();) { System.out.println(resultSet.getString(1) + " " + resultSet.getString(2)); } /* try { if (resultSet != null) { resultSet.close(); } } catch (SQLException e){ e.printStackTrace(); } */ try { if (stmt != null) { stmt.close(); } } catch (SQLException e){ e.printStackTrace(); } } } catch (SQLException e) { System.out.println("Error code: " + e.getErrorCode()); System.out.println("SQL state: " + e.getSQLState()); e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } finally { try { if (resultSet != null) { resultSet.close(); } } catch (SQLException e){ e.printStackTrace(); } try { if (stmt != null) { stmt.close(); } } catch (SQLException e){ e.printStackTrace(); } try { if (conn != null) { conn.close(); } } catch (SQLException e){ e.printStackTrace(); } } } }
- InfiniteLooperWithRsClosed.java
import java.sql.Connection; import java.sql.DriverManager; import java.sql.Statement; import java.sql.ResultSet; import java.sql.SQLException; public class InfiniteLooperWithRsClosed { public static void main(String args[]) { Connection conn = null; Statement stmt = null; ResultSet resultSet = null; try { Class.forName ("oracle.jdbc.driver.OracleDriver"); conn = DriverManager.getConnection ("jdbc:oracle:thin:@192.168.45.101:1521:orcl","scott","tiger"); for(;;) { stmt = conn.createStatement(); resultSet = stmt.executeQuery("select id, name from emp"); for(;resultSet.next();) { System.out.println(resultSet.getString(1) + " " + resultSet.getString(2)); } try { if (resultSet != null) { resultSet.close(); } } catch (SQLException e){ e.printStackTrace(); } /* try { if (stmt != null) { stmt.close(); } } catch (SQLException e){ e.printStackTrace(); } */ } } catch (SQLException e) { System.out.println("Error code: " + e.getErrorCode()); System.out.println("SQL state: " + e.getSQLState()); e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } finally { try { if (resultSet != null) { resultSet.close(); } } catch (SQLException e){ e.printStackTrace(); } try { if (stmt != null) { stmt.close(); } } catch (SQLException e){ e.printStackTrace(); } try { if (conn != null) { conn.close(); } } catch (SQLException e){ e.printStackTrace(); } } } }