Rainbow Engine

IT技術を分かりやすく簡潔にまとめることによる学習の効率化、また日常の気付きを記録に残すことを目指します。

Java OracleDB

JavaのJDBC接続でjava.sql.SQLRecoverableException: Closed Connectionが発生した時の解決メモ

投稿日:2020年1月26日 更新日:

(0)目次&概説

(1) エラー事象の概要
 (1-1) エラーの発生状況
 (1-2) エラーメッセージ全文
(2) エラーの原因
(3) エラーの対処方法
 (3-1) エラーの修正内容
 (3-2) 修正前ソース(Before Fix)
 (3-3) 修正前ソース実行結果
 (3-3) 修正後ソース(After Fix)
 (3-4) 修正後ソース解説
 (3-5) 修正後ソース実行結果
(4) 追加で考慮すべき点
 (4-1) finally句でclose処理をしていないと、クローズ漏れになる恐れがある
  (4-1-1) 明示的にcloseした方が良い理由
 (4-2) finally句でclose処理をする場合、SQLExceptionの考慮が必要

(1) エラー事象の概要

(1-1) エラーの発生状況

JDBC接続でサーブレットからDBへの接続時に「java.sql.SQLRecoverableException: Closed Connection」が発生した際の対処方法について記載します。私のケースでは、1つに纏まっていたDB接続の処理を共通部品化するために複数メソッドに分割(Connection、Statement、ResultSet)しようとした所、このエラーに遭遇しました。 

java.sql.SQLRecoverableException: Closed Connection

 

(図1)

(1-2) エラーメッセージの全文

java.sql.SQLRecoverableException: Closed Connection
	at oracle.jdbc.driver.PhysicalConnection.createStatement(PhysicalConnection.java:3450)
	at oracle.jdbc.driver.PhysicalConnection.createStatement(PhysicalConnection.java:3425)
	at login.DbConnectTest3.DbStatement(DbConnectTest3.java:43)
	at login.DbConnectTest3.doGet(DbConnectTest3.java:72)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:634)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:741)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:199)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:610)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:137)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81)
	at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:660)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:798)
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:808)
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1498)
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
	at java.lang.Thread.run(Thread.java:748)
Jan 26, 2020 4:00:55 PM org.apache.catalina.core.StandardWrapperValve invoke
SEVERE: Servlet.service() for servlet [login.DbConnectTest3] in context with path [/LoginForms] threw exception
java.lang.NullPointerException
	at login.DbConnectTest3.DbQuery(DbConnectTest3.java:53)
	at login.DbConnectTest3.doGet(DbConnectTest3.java:73)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:634)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:741)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:199)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:610)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:137)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81)
	at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:660)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:798)
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:808)
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1498)
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
	at java.lang.Thread.run(Thread.java:748)

 

目次にもどる

(2) エラーの原因

「try-with-resources」文はそのスコープの範囲を抜ける際にcloseメソッドを自動で呼んでるために「Closed Connection」が発生していました(次の例1・例2)。なので「try-with-resources」の条件文に記載したような、ConnectionオブジェクトやStatementオブジェクトをインスタンス化する部分は、try文を抜けたタイミングでCloseされてしまい、次のメソッドで利用する時にはcloseされていた様です。

(例1)

try(Connection conn_temp = dbms.getConn();){

 

(例2)

try(Statement stmt = conn.createStatement();){

 

目次にもどる

(3) エラーの対処方法

(3-1) エラーの修正内容

応急対処として、通常の「try-catch」文で記述すればエラーは消えました。

(図2)修正例
左側が「修正前」で、右側が「修正後」です。修正はいずれも「try-with-resources」の条件文に記載していたConnectionオブジェクトやStatementオブジェクトのインスタンス化を、try{ }文の内部に移すという内容です。

No 修正前 修正後
1 try(Connection conn_temp = dbms.getConn();){
   conn = conn_temp;
try{
   conn = dbms.getConn();
2 try(Statement stmt = conn.createStatement();){
   stmt = stmt_tmp;
try{
   stmt = conn.createStatement();

 

(3-2) 修正前ソース(Before Fix)

package login;

import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import database.DBMS;

/**
 * Servlet implementation class DbConnectTest1
 */
@WebServlet("/DbConnectTest3")
public class DbConnectTest3 extends HttpServlet {
	private static final long serialVersionUID = 1L;
    public DbConnectTest3() {super();}    
	public Connection DbConnect (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		Connection conn = null;
		DBMS dbms = new DBMS();
		try(Connection conn_temp = dbms.getConn();){
			conn = conn_temp;
		} catch (ClassNotFoundException e1) {
			e1.printStackTrace();
		} catch (SQLException e2) {
			e2.printStackTrace();
		}
		return conn;
	}
	public Statement DbStatement (HttpServletRequest request, HttpServletResponse response, Connection conn) throws ServletException, IOException {
		Statement stmt = null;
		try(Statement stmt_tmp = conn.createStatement();){
			stmt = stmt_tmp;
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return stmt;
	}
	public ResultSet DbQuery (HttpServletRequest request, HttpServletResponse response, Statement stmt, String sql) throws ServletException, IOException {
		ResultSet rs = null;
		try{
			rs = stmt.executeQuery(sql);
		} 
		catch (SQLException e) {
			e.printStackTrace();
		}
		return rs;
	}
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {		
		
    	response.setContentType("text/html; charset=UTF-8");
		PrintWriter out = response.getWriter();
		out.println("<html><body> Hello Test !!!<br />");
    	String select1 = "SELECT * FROM ACCOUNT";
    	
    	Connection conn1 = null;
    	Statement stmt1 = null;
    	ResultSet rs1 = null;
    	
    	conn1 = DbConnect(request,response);
    	stmt1 = DbStatement(request,response,conn1);
    	rs1 = DbQuery(request,response,stmt1,select1);
		try {
			while (rs1.next()) {
				out.println("AC= "+rs1.getInt("ACCOUNT_ID")+" BAL= "+rs1.getInt("AVAIL_BALANCE")+"<br />");
			}
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			try {
				if(rs1 != null) rs1.close(); 
				if(stmt1 != null) stmt1.close();
				if(conn1 != null) conn1.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		out.println("</body></html>");
	}
    
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}
}

 

(3-3) 修正前ソース実行結果

(図3)冒頭のエラーが発生

(図4)画面もSQLの結果が表示されず・・

目次にもどる

(3-4) 修正後ソース(After Fix)

package login;

import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import database.DBMS;

/**
 * Servlet implementation class DbConnectTest1
 */
@WebServlet("/DbConnectTest2")
public class DbConnectTest2 extends HttpServlet {
	private static final long serialVersionUID = 1L;
    public DbConnectTest2() {super();}    
	public Connection DbConnect (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		Connection conn = null;
		DBMS dbms = new DBMS();
		try{
			conn = dbms.getConn();
		} catch (ClassNotFoundException e1) {
			e1.printStackTrace();
		} catch (SQLException e2) {
			e2.printStackTrace();
		}
		return conn;
	}
	public Statement DbStatement (HttpServletRequest request, HttpServletResponse response, Connection conn) throws ServletException, IOException {
		Statement stmt = null;
		try{
			stmt = conn.createStatement();
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return stmt;
	}
	public ResultSet DbQuery (HttpServletRequest request, HttpServletResponse response, Statement stmt, String sql) throws ServletException, IOException {
		ResultSet rs = null;
		try{
			rs = stmt.executeQuery(sql);
		} 
		catch (SQLException e) {
			e.printStackTrace();
		}
		return rs;
	}
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {		
		
    	response.setContentType("text/html; charset=UTF-8");
		PrintWriter out = response.getWriter();
		out.println("<html><body> Hello Test !!!<br />");
    	String select1 = "SELECT * FROM ACCOUNT";
    	
    	Connection conn1 = null;
    	Statement stmt1 = null;
    	ResultSet rs1 = null;
    	
    	conn1 = DbConnect(request,response);
    	stmt1 = DbStatement(request,response,conn1);
    	rs1 = DbQuery(request,response,stmt1,select1);
		try {
			while (rs1.next()) {
				out.println("AC= "+rs1.getInt("ACCOUNT_ID")+" BAL= "+rs1.getInt("AVAIL_BALANCE")+"<br />");
			}
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			try {
				if(rs1 != null) rs1.close(); 
				if(stmt1 != null) stmt1.close();
				if(conn1 != null) conn1.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		out.println("</body></html>");
	}
    
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}
}

 

(3-4) 修正後ソース解説

行数 処理概要 備考
24行~35行目 DBMSオブジェクトをインスタンス化し、それを使いConnectionオブジェクトもインスタンス化しています。またtry-catchで例外処理をしており、特にtryには条件文をしてせずに記述しています。 メソッドの設計方針として、後にcloseしやすいように、Connectionオブジェクトを返り値として返却しています。
(Connection~Statement~ResultSetまで全て行ってしまうと各々を個別にCloseできなくなってしまうため、メソッドを分けている)
36行~44行目 ステートメントを作成するメソッドです。
引数にConnectionオブジェクトを貰う形式にしています(メソッドの中でDbConnectを呼ぶのではなく、引数としてConnectionオブジェクトを貰っている)。
この理由は上記と被りますが、後にcloseしやすいようにDbConnectは内部では呼ばず、実際に使うタイミングで直接呼んで、使い終わったらその場で即座にCloseできるようにするためです(内部に埋め込まれているとcloseしにくい)。
 
45行~54行目 ResultSet(クエリの結果)を取得するメソッドです。こちらも同様にtry-catchで例外処理をしており、特にtryには条件文をしてせずに記述しています。
そして上記2メソッドと同じく、引数にStatementオブジェクトを受け取る事で必要な時に呼んでその場でcloseしやすい形式にしました。
 
55行~85行目 doGetメソッドです。
62行~68行目でConnection/Statement/ResultSetの初期化し、メソッドを呼んでインスタンス化しています。
69行~85行目ではtry-catch-finally句でResultSetからSQLの結果を取得しています。Finally句の中で使い終わったConnection/Statement/ResultSetをクローズしています。クローズ時もSQLExceptionの例外発生の可能性があるため、try-catchで囲んでいます。
 

目次にもどる

(3-5) 修正後ソース実行結果

(図5)エラーは解消

(図6)SQL結果も表示!

目次にもどる

(4) 追加で考慮すべき点

上記の方法でエラー自体は消えるものの、他にも考慮すべき点を記載します。

(4-1) finally句でclose処理をしていないと、クローズ漏れになる恐れがある

基本はConnectionオブジェクト、Statementオブジェクト、ResultSetオブジェクトを全てリリースします。なので「try-with-resources」を使わない場合は明示的にclose()を行う必要があります。

(4-1-1) 明示的にcloseした方が良い理由

Statementを作成するとDBによってはカーソルがOpenになりますが、Closeされるまではメモリが確保され続け、DB側でもカーソルが開きっぱなしになってしまうので閉じる必要があります。

通常は上位オブジェクトを閉じれば、下位オブジェクトも自動的に閉じられる機能があるものの(Connection>Statement>ResultSet 左に記載ほど上位)、以前OracleでConnectionを閉じたにも関わらずカーソル(ResultSet)が開いたままになり「Maximum open cursors exceeded.」のようなエラーが発生する事象があったため、ResultSetやStatementも明示的に閉じる方が安全と認識されるようになりました。

(4-2) finally句でclose処理をする場合、SQLExceptionの考慮が必要

finally句でclose処理をする場合、クローズ不可時の例外としてSQLExceptionをスローするため、クローズの処理もtry、catchで囲む必要があります。

目次にもどる

Adsense審査用広告コード


Adsense審査用広告コード


-Java, OracleDB

執筆者:


comment

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

関連記事

Servlet/JSPで日本語文字が「???」になる問題とFilterの活用について

(0)目次&概説 (1) 事象 (2) 原因 (3) 対処方法1  (3-1) フィルタクラスの新規作成  (3-2) フィルタクラスへのコード追加  (3-3) 疎通確認テスト (4) 対処方法2 …

Servlet(サーブレット)におけるフォワード(forward)とリダイレクト(redirect)の違い

<目次> (1) Servlet(サーブレット)におけるフォワード(forward)とリダイレクト(redirect)の違い  (1-1) フォワード(forward)とは?  (1-2) リダイレク …

Oracle DBで「ORA-09925: Unable to create audit trail file」エラーが出た時の対処方法について

<目次> (1) Oracle DBで「ORA-09925: Unable to create audit trail file」エラーが出た時の対処方法について  (1-1) エラー概要  (1-2 …

Javaのenumとは?使い方や意味を様々な利用シーンでご紹介(if、for、switch他)

<目次> (1) Javaのenumとは?意味や用途を様々な利用シーンでご紹介   (1-1) enumとは?  (1-2) 構文(enumの定義)  (1-3) 様々なenumの使用例 (1) Ja …

オープンアドレス法をJavaで実装したプログラムのサンプルをご紹介

<目次> (1) オープンアドレス法をJavaで実装したプログラムのサンプルをご紹介  (1-1) オープンアドレス法とは  (1-2) Javaのサンプルプログラムの全体像  (1-3) Javaの …

  • English (United States)
  • 日本語
Top