JDBCでのバッチ処理

1前書き

Java Database Connectivity(JDBC)は、データベースと対話するために使用されるJava APIです。バッチ処理では、複数のクエリを1つの単位にまとめて、1回のネットワーク旅行でデータベースに渡します。

この記事では、JDBCをSQL照会のバッチ処理に使用する方法を説明します。

JDBCの詳細については、紹介記事のリンク/java-jdbc[こちら]を参照してください。

** 2なぜバッチ処理なのか

パフォーマンスとデータの整合性がバッチ処理を実行する主な目的です。

2.1. 改良された性能

ユースケースによっては、データベーステーブルに大量のデータを挿入する必要があります。 JDBCを使用しながら、バッチ処理なしでこれを実現する方法の1つは、複数のクエリを順番に実行することです。

データベースに送信される順次クエリの例を見てみましょう。

statement.execute("INSERT INTO EMPLOYEE(ID, NAME, DESIGNATION) "
 + "VALUES ('1','EmployeeName1','Designation1')");
statement.execute("INSERT INTO EMPLOYEE(ID, NAME, DESIGNATION) "
 + "VALUES ('2','EmployeeName2','Designation2')");

これらのシーケンシャルコールはデータベースへのネットワークトリップの回数を増加させ、パフォーマンスの低下を招きます。

バッチ処理を使用すると、これらのクエリを1回の呼び出しでデータベースに送信できるため、パフォーマンスが向上します。

2.2. データの一貫性

状況によっては、データを複数のテーブルにプッシュする必要があります。

これにより、プッシュされる一連のクエリが重要な相互関係のあるトランザクションが発生します。

実行中にエラーが発生した場合、前のクエリによってプッシュされたデータがあればロールバックされます。

複数のテーブルにデータを追加する例を見てみましょう。

statement.execute("INSERT INTO EMPLOYEE(ID, NAME, DESIGNATION) "
 + "VALUES ('1','EmployeeName1','Designation1')");
statement.execute("INSERT INTO EMP__ADDRESS(ID, EMP__ID, ADDRESS) "
 + "VALUES ('10','1','Address')");

上記のアプローチにおける典型的な問題は、最初のステートメントが成功し、2番目のステートメントが失敗するときに発生します。 ** この状況では、最初のステートメントによって挿入されたデータのロールバックがなく、データの不整合が生じます。

トランザクションを複数の挿入/更新にまたがって終了した後でトランザクションをコミットするか、例外が発生した場合はロールバックを実行することでデータの一貫性を実現できますが、この場合はステートメントごとにデータベースを繰り返しヒットします。

3バッチ処理の仕方

JDBCには、データベースでクエリを実行するための Statement PreparedStatement の2つのクラスがあります。どちらのクラスも、 addBatch() メソッドと executeBatch() メソッドを独自に実装しており、これらのメソッドによってバッチ処理機能が提供されます。

3.1. Statement を使用したバッチ処理

JDBCでは、データベースに対してクエリを実行する最も簡単な方法は Statement オブジェクト __. __を使うことです。

まず、 addBatch() を使用して、すべてのSQLクエリをバッチに追加し、 executeBatch() を使用してそれらのSQLクエリを実行します。

executeBatch() の戻り値の型は int 配列で、各SQL文の実行によって影響を受けたレコード数を示します。

Statementを使用してバッチを作成して実行する例を見てみましょう。

Statement statement = connection.createStatement();
statement.addBatch("INSERT INTO EMPLOYEE(ID, NAME, DESIGNATION) "
 + "VALUES ('1','EmployeeName','Designation')");
statement.addBatch("INSERT INTO EMP__ADDRESS(ID, EMP__ID, ADDRESS) "
 + "VALUES ('10','1','Address')");
statement.executeBatch();

上記の例では、 Statement を使用して EMPLOYEE テーブルと EMP ADDRESS__テーブルにレコードを挿入しようとしています。 SQLクエリが実行されるバッチに追加されている様子がわかります。

3.2. PreparedStatement を使用したバッチ処理

PreparedStatement は、SQLクエリを実行するために使用されるもう1つのクラスです。 __. __これは、SQLステートメントの再利用を可能にし、各更新/挿入に対して新しいパラメータを設定することを要求します。

PreparedStatementを使用した例を見てみましょう。まず、 StringとしてエンコードされたSQLクエリを使用してステートメントを設定します。

String[]EMPLOYEES = new String[]{"Zuck","Mike","Larry","Musk","Steve"};
String[]DESIGNATIONS = new String[]{"CFO","CSO","CTO","CEO","CMO"};

String insertEmployeeSQL = "INSERT INTO EMPLOYEE(ID, NAME, DESIGNATION) "
 + "VALUES (?,?,?)";
PreparedStatement employeeStmt = connection.prepareStatement(insertEmployeeSQL);

次に、 String 値の配列をループ処理して、新しく構成したクエリをバッチに追加します。

ループが終了したら、バッチを実行します。

for(int i = 0; i < EMPLOYEES.length; i++){
    String employeeId = UUID.randomUUID().toString();
    employeeStmt.setString(1,employeeId);
    employeeStmt.setString(2,EMPLOYEES[i]);
    employeeStmt.setString(3,DESIGNATIONS[i]);
    employeeStmt.addBatch();
}
employeeStmt.executeBatch();

上記の例では、 PreparedStatementを使用して EMPLOYEE__テーブルにレコードを挿入しています。挿入する値がクエリでどのように設定され、実行されるバッチに追加されるかを確認できます。

4結論

この記事では、JDBCを使用してデータベースと対話するときに、SQL照会のバッチ処理がどのように重要であるかを見ました。

いつものように、この記事に関連するコードはhttps://github.com/eugenp/tutorials/tree/master/persistence-modules/core-java-persistence[over Github]で見つけることができます。