LoginSignup
0
0

More than 3 years have passed since last update.

Db2を利用したJavaアプリケーションでERRORCODE=-4471が発生する。

Posted at

環境

本記事を書くにあたって利用した主なソフトウェアのバージョンは次の通りです。

  • javac 11.0.4
  • openjdk version "11.0.4" 2019-07-16
  • IBM DB2 Developer-C Edition 11.5 (Docker/wsl2)
  • IBM Data Server Driver for JDBC and SQLJ 4.26.14

事象

以下のコードはEMPというテーブルからすべてのEMPNOを取得し、標準出力に出力するというものです。

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class Main {
    public static void main(String[] args) {
        String url = "jdbc:db2://<hostname>:<port>/<dbname>";
        String user = "<user>";
        String password = "<password>";

        try (Connection con = DriverManager.getConnection(url, user, password)) {
            con.setAutoCommit(false);

            PreparedStatement ps = con.prepareStatement("SELECT * FROM EMP");
            ResultSet rs = ps.executeQuery();

            while (rs.next()) {
                System.out.println(rs.getString("EMPNO"));
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

このソースコードをコンパイルし、実行すると、標準出力にすべてのEMPNOを出力したあと、次のような例外が発生します。

com.ibm.db2.jcc.am.SqlException: [jcc][t4][10251][10308][4.26.14] 接続でのトランザクション進行中に java.sql.Connection.close() が要求されました。
トランザクションはアクティブのままとなり、接続はクローズできません。 ERRORCODE=-4471, SQLSTATE=null
    at com.ibm.db2.jcc.am.b7.a(b7.java:794)
    at com.ibm.db2.jcc.am.b7.a(b7.java:66)
    at com.ibm.db2.jcc.am.b7.a(b7.java:133)
    at com.ibm.db2.jcc.am.Connection.checkForTransactionInProgress(Connection.java:1484)
    at com.ibm.db2.jcc.t4.b.checkForTransactionInProgress(b.java:7581)
    at com.ibm.db2.jcc.am.Connection.closeResourcesX(Connection.java:1507)
    at com.ibm.db2.jcc.am.Connection.closeX(Connection.java:1493)
    at com.ibm.db2.jcc.am.Connection.close(Connection.java:1470)
    at Main.main(Main.java:22)

java.sql.Connection.close()try-catch-with-resourceが勝手に呼び出してくれているから理解できるとして、そもそも「トランザクションはアクティブのままとなり、接続はクローズできません」ってなんのこっちゃ??? となるわけです。

原因

Db2ではDBコネクションをクローズする前に、COMMITROLLBACKによってトランザクションを確定させる必要があります。COMMITROLLBACKをするのは、INSERTDELETEなど、DBのデータに変更を加えたときだけで、データを参照するだけのSELECTではCOMMITROLLBACKに注意が回らない--という人も多いかと思いますが、少なくともDb2ではSELECTの場合でもトランザクションを意識する必要があります。

前述のソースコードでは、自動コミットがオフの状態になっていました(con.setAutoCommit(false))。自動コミット=trueの場合、Connection::closeを呼び出すと、jdbcドライバが自動でコミットしてくれるのですが、自動コミット=falseだと、プログラマが明示的にConnection::commitConnection::rollbackを呼び出さないと、トランザクションが確定しません。つまり、前述のソースコードでは自動コミットをオフにしたために、トランザクションが確定しないまま、コネクションをクローズしようとし、その結果ERRORCODE=-4471が発生してしまったわけです。

対策

  • Connection::commitもしくはConnection::rollbackを呼び出して、トランザクションを明示的に完了させる
  • 自動コミット=trueにして、トランザクションの完了をJDBC Driverに丸投げする。

対策はこんなもんでしょうか(´・ω・`)

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0