1. 序章

Java Database Connectivity(JDBC)は、データベースとの対話に使用されるJavaAPIです。 バッチ処理は、複数のクエリを1つのユニットにグループ化し、1回のネットワークトリップでデータベースに渡します。

この記事では、SQLクエリのバッチ処理にJDBCを使用する方法について説明します。

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には、データベースでクエリを実行するためのStatementPreparedStatementの2つのクラスがあります。 どちらのクラスにも、バッチ処理機能を提供する addBatch()メソッドと executeBatch()メソッドの独自の実装があります。

3.1. ステートメントを使用したバッチ処理

JDBCを使用する場合、データベースでクエリを実行する最も簡単な方法は、 声明物体

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

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

ステートメントを使用してバッチを作成および実行する例を見てみましょう。

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を使用したバッチ処理

プリペアドステートメント SQLクエリの実行に使用される別のクラスですSQLステートメントの再利用が可能になり、更新/挿入ごとに新しいパラメーターを設定する必要があります。

を使った例を見てみましょうプリペアドステートメント。 まず、としてエンコードされた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();

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

4. 結論

この記事では、JDBCを使用してデータベースを操作する際に、SQLクエリのバッチ処理がどのように重要であるかを説明しました。

いつものように、この記事に関連するコードは、Githubにあります。