JDBCでのバッチ処理
1. 前書き
Java Database Connectivity(JDBC)は、データベースとの対話に使用されるJava APIです。 バッチ処理は、複数のクエリを1つのユニットにグループ化し、1回のネットワークトリップでデータベースに渡します。
この記事では、SQLクエリのバッチ処理にJDBCを使用する方法について説明します。
JDBCの詳細については、紹介記事hereを確認してください。
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番目のステートメントが失敗したときに発生します。 In this situation there is no rollback of the data inserted by the first statement, leading to data inconsistency.
複数の挿入/更新にまたがるトランザクションにまたがり、最後にトランザクションをコミットするか、例外が発生した場合にロールバックを実行することで、データの整合性を実現できますが、この場合でも、ステートメントごとにデータベースに繰り返しアクセスしています。
3. バッチ処理の方法
JDBCには、データベースでクエリを実行するための2つのクラスStatementとPreparedStatementが用意されています。 どちらのクラスにも、バッチ処理機能を提供するaddBatch()メソッドとexecuteBatch()メソッドの独自の実装があります。
3.1. Statementを使用したバッチ処理
JDBCを使用する場合、データベースでクエリを実行する最も簡単な方法は、Statementオブジェクト.を使用することです。
まず、addBatch()を使用して、すべてのSQLクエリをバッチに追加してから、executeBatch()を使用してそれらのSQLクエリを実行できます。
executeBatch()の戻り値の型は、各SQLステートメントの実行によって影響を受けたレコードの数を示すint配列です。
ステートメントを使用してバッチを作成および実行する例を見てみましょう。
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クエリの実行に使用される別のクラスです. 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クエリのバッチ処理がどのように重要であるかを説明しました。
いつものように、この記事に関連するコードはover on Githubにあります。