こんにちは。開発ブログ運営担当のktです。

JDBCのバッチ更新を利用したことがありますか?

データベースへの送信回数を減らすことでパフォーマンスアップさせる方法ですが、

以前紹介したWITH句と同じで利用したことがない人が意外と周りにいます。

今回はバッチ更新を利用する方法を紹介します。

前提

画面から入力されたデータやCSVファイルから一括で読み込んだデータを、

パラメータが違うだけで同じinsert文やupdate文で複数レコードを更新することがあると思います。

[従業員No]、[名前]のカラムを持つ【従業員テーブル】に、

従業員Listの件数分insertする場合を例に説明したいと思います。

バッチ更新を利用しないパターン

PreparedStatementでパラメータを設定してexecuteUpdateメソッドをfor文で繰り返し実行します。

executeUpdateを実行するたびデータベースとの通信が発生します。

String sql = "insert into 従業員テーブル (従業員No, 名前) values (?, ?)";
PreparedStatement statement = connection.prepareStatement(sql);
for (従業員Bean bean : 従業員List) {
    statement.setString(1, bean.get従業員No);
    statement.setString(2, bean.get名前);
    statement.executeUpdate();
}

※connectionのクローズや例外処理等は省略しています。

バッチ更新を利用するパターン

addBatchメソッドで追加し、ある程度実行するSQL文が溜まったらexecuteBatchメソッドで実行します。

1回のデータベースとの通信で溜まっていたSQL文全てを実行するので、

通信のオーバーヘッドが少なくなりパフォーマンスがアップします。

String sql = "insert into 従業員テーブル (従業員No, 名前) values (?, ?)";
PreparedStatement statement = connection.prepareStatement(sql);
int cnt = 0;
int listCnt = 従業員List.size();
for (従業員Bean bean : 従業員List) {
    statement.setString(1, bean.get従業員No);
    statement.setString(2, bean.get名前);
    statement.addBatch();
    cnt++;
    if (cnt % 100 == 0 || cnt == listCnt) {
        statement.executeBatch();
    }
}

まとめ

例では100件溜まるたびにexecuteBatchメソッドを実行するようにしています。

溜めすぎても逆にパフォーマンスが悪くなる場合があるようです。

下記サイトで検証してくれています。

JDBC経由で100万件取得・追加してみた

こういったまとめてSQLを実行する方法は、

MyBatis等のO/Rマッパーにも用意されています。

一括申請や一括承認といったワークフロー系のシステムや、

CSVを読み込んでインポートするといった一括で処理したいという要望は多いと思います。

そうした時に少しでもパフォーマンスを良くして、

ユーザーに気持ちよく使ってもらえるようにしたいですね。