LoginSignup
2
1

More than 3 years have passed since last update.

executeBatchの戻り値の内容が11gと12cで異なる

Posted at

実行環境

本記事を書くにあたって、利用した主なソフトウェアのバージョンは次の通りです。なおOracle Databaseの構築にあたってはDockerおよびOracle公式のDocker Imageを利用しています。

software version, edition
Oracle Database 11g Oracle Database 11g Express Edition Release 11.2.0.2.0 - 64bit Production
Oracle Database 12c Oracle Database 12c Standard Edition Release 12.2.0.1.0 - 64bit Production
ojdbc6.jar (11g JDBC Driver) Oracle 11.2.0.2.0 JDBC 4.0 compiled with JDK6 on Sat_Aug_14_12:18:34_PDT_2010
ojdbc8.jar (12c JDBC Driver) Oracle 12.2.0.1.0 JDBC 4.2 compiled with javac 1.8.0_91 on Tue_Dec_13_06:08:31_PST_2016
javac javac 11.0.4
java openjdk version "11.0.4" 2019-07-16

事象概要

JavaアプリケーションのデータベースをOracle Database 11cからOracle Database 12gにバージョンアップするにあたって、PreparedStatement::executeBatchの戻り値(int型配列)の内容が微妙に異なり、時間を無限に溶かしたので、メモを残しておきます。

以下のソースコードは、バッチ更新を利用してテーブルUSERSに3件のデータを挿入するというものです。データベースへの接続情報はすべて実行時引数として受け取ることとします。

Main.java
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        String url = args[0];
        String user = args[1];
        String password = args[2];

        try (Connection c = DriverManager.getConnection(url, user, password);
             PreparedStatement ps = c.prepareStatement("INSERT INTO USERS (ID, NAME) VALUES(?, ?)")) {
            ps.setInt(1, 1);
            ps.setString(2, "Alice");
            ps.addBatch();

            ps.setInt(1, 2);
            ps.setString(2, "Bob");
            ps.addBatch();

            ps.setInt(1, 3);
            ps.setString(2, "Carol");
            ps.addBatch();

            int[] updateCounts = ps.executeBatch();
            System.out.println(Arrays.toString(updateCounts));
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

このコードをコンパイルしたのち、まずは接続先を11gに向けて実行、次に接続先を12cに変えて実行した結果が次の通りです。

$ java -cp ../lib/ojdbc6.jar:. Main jdbc:oracle:thin:@192.168.99.100:1511:xe user1 password
[-2, -2, -2]
$ java -cp ../lib/ojdbc8.jar:. Main jdbc:oracle:thin:@192.168.99.100:1512/ORCLPDB1 user1 password
[1, 1, 1]

PreparedStatement::executeBatchの戻り値であるupdateCountsの内容が11gと12cで異なることが確認できました。

12cについては、内容は明白で、更新した件数がint型の配列に格納されています。一方、11gのexecuteBatchStatement.SUCCESS_NO_INFOを格納した配列を戻り値とします。

結論: ドキュメントはちゃんと読もう

実はこの挙動の違いはOracle Databaseのドキュメントを読むと、ちゃんと書かれています。

まずは11gのドキュメント:

文バッチが正常に処理された場合、文のexecuteBatchコールから戻される整数配列、つまり、更新件数配列には、常にバッチ操作1つに対して1つの要素が含まれます。標準バッチ更新のOracle実装では、配列要素の値は次のようになります。
プリコンパイルされたSQL文のバッチの場合、バッチに含まれている個々の文によって影響を受けたデータベースの行数はわかりません。そのため、配列要素の値はすべて-2になります。JDBC 2.0仕様によれば、値-2は、操作は正常終了したが影響を受けた行数は不明であることを示します。

一方12cのドキュメントには11gとは挙動が異なることが明記されています、

Oracle Database 12cリリース1 (12.1)以降、executeBatchメソッドは改良され、バッチのレコード数と同じサイズのint配列を戻します。戻り配列の各項目は、バッチの対応するレコードの影響を受けたデータベース表の行数です。

ちなみに実際に試していませんが、Oracle Database 18cのドキュメント
には、上記の12cのそれとまったく同じ文言が掲載されています。つまり12cと18cではPreparedStatement::executeBatchの挙動に差はないと思われます。

2
1
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
2
1