JDBC バッチ操作
ほとんどの JDBC ドライバーは、同じ準備済みステートメントへの複数の呼び出しをバッチ処理すると、パフォーマンスが向上します。更新をバッチにグループ化することにより、データベースへのラウンドトリップの回数を制限します。
JdbcTemplate
を使用した基本的なバッチ操作
JdbcTemplate
バッチ処理を実現するには、特別なインターフェース BatchPreparedStatementSetter
の 2 つのメソッドを実装し、その実装を batchUpdate
メソッド呼び出しの 2 番目のパラメーターとして渡します。getBatchSize
メソッドを使用して、現在のバッチのサイズを提供できます。setValues
メソッドを使用して、準備済みステートメントのパラメーターの値を設定できます。このメソッドは、getBatchSize
呼び出しで指定した回数と呼ばれます。次の例では、リストのエントリに基づいて t_actor
テーブルを更新し、リスト全体がバッチとして使用されます。
Java
Kotlin
public class JdbcActorDao implements ActorDao {
private JdbcTemplate jdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
public int[] batchUpdate(final List<Actor> actors) {
return this.jdbcTemplate.batchUpdate(
"update t_actor set first_name = ?, last_name = ? where id = ?",
new BatchPreparedStatementSetter() {
public void setValues(PreparedStatement ps, int i) throws SQLException {
Actor actor = actors.get(i);
ps.setString(1, actor.getFirstName());
ps.setString(2, actor.getLastName());
ps.setLong(3, actor.getId().longValue());
}
public int getBatchSize() {
return actors.size();
}
});
}
// ... additional methods
}
class JdbcActorDao(dataSource: DataSource) : ActorDao {
private val jdbcTemplate = JdbcTemplate(dataSource)
fun batchUpdate(actors: List<Actor>): IntArray {
return jdbcTemplate.batchUpdate(
"update t_actor set first_name = ?, last_name = ? where id = ?",
object: BatchPreparedStatementSetter {
override fun setValues(ps: PreparedStatement, i: Int) {
ps.setString(1, actors[i].firstName)
ps.setString(2, actors[i].lastName)
ps.setLong(3, actors[i].id)
}
override fun getBatchSize() = actors.size
})
}
// ... additional methods
}
更新のストリームを処理したり、ファイルから読み込んだりする場合、希望するバッチサイズがありますが、最後のバッチにはその数のエントリがない場合があります。この場合、InterruptibleBatchPreparedStatementSetter
インターフェースを使用できます。これにより、入力ソースが使い果たされると、バッチを中断できます。isBatchExhausted
メソッドを使用すると、バッチの終了を通知できます。
オブジェクトのリストを使用したバッチ操作
JdbcTemplate
と NamedParameterJdbcTemplate
の両方が、バッチ更新を提供する代替メソッドを提供します。特別なバッチインターフェースを実装する代わりに、呼び出しのすべてのパラメーター値をリストとして提供します。フレームワークはこれらの値をループ処理し、内部準備済みステートメント setter を使用します。API は、名前付きパラメーターを使用するかどうかによって異なります。名前付きパラメーターに対して、SqlParameterSource
の配列を提供します。これは、バッチのメンバーごとに 1 つのエントリです。SqlParameterSourceUtils.createBatch
簡易メソッドを使用してこの配列を作成し、Bean スタイルオブジェクトの配列(getter メソッドがパラメーターに対応する)、String
-keyed Map
インスタンス(対応するパラメーターを値として含む)、または両方の組み合わせを渡すことができます。
次の例は、名前付きパラメーターを使用したバッチ更新を示しています。
Java
Kotlin
public class JdbcActorDao implements ActorDao {
private NamedParameterTemplate namedParameterJdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
}
public int[] batchUpdate(List<Actor> actors) {
return this.namedParameterJdbcTemplate.batchUpdate(
"update t_actor set first_name = :firstName, last_name = :lastName where id = :id",
SqlParameterSourceUtils.createBatch(actors));
}
// ... additional methods
}
class JdbcActorDao(dataSource: DataSource) : ActorDao {
private val namedParameterJdbcTemplate = NamedParameterJdbcTemplate(dataSource)
fun batchUpdate(actors: List<Actor>): IntArray {
return this.namedParameterJdbcTemplate.batchUpdate(
"update t_actor set first_name = :firstName, last_name = :lastName where id = :id",
SqlParameterSourceUtils.createBatch(actors));
}
// ... additional methods
}
従来の ?
プレースホルダーを使用する SQL ステートメントの場合、更新値を含むオブジェクト配列を含むリストを渡します。このオブジェクト配列には、SQL ステートメントのプレースホルダーごとに 1 つのエントリが必要であり、SQL ステートメントで定義されている順序と同じ順序である必要があります。
次の例は、従来の JDBC ?
プレースホルダーを使用することを除いて、前述の例と同じです。
Java
Kotlin
public class JdbcActorDao implements ActorDao {
private JdbcTemplate jdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
public int[] batchUpdate(final List<Actor> actors) {
List<Object[]> batch = new ArrayList<>();
for (Actor actor : actors) {
Object[] values = new Object[] {
actor.getFirstName(), actor.getLastName(), actor.getId()};
batch.add(values);
}
return this.jdbcTemplate.batchUpdate(
"update t_actor set first_name = ?, last_name = ? where id = ?",
batch);
}
// ... additional methods
}
class JdbcActorDao(dataSource: DataSource) : ActorDao {
private val jdbcTemplate = JdbcTemplate(dataSource)
fun batchUpdate(actors: List<Actor>): IntArray {
val batch = mutableListOf<Array<Any>>()
for (actor in actors) {
batch.add(arrayOf(actor.firstName, actor.lastName, actor.id))
}
return jdbcTemplate.batchUpdate(
"update t_actor set first_name = ?, last_name = ? where id = ?", batch)
}
// ... additional methods
}
前に説明したすべてのバッチ更新メソッドは、各バッチエントリの影響を受ける行の数を含む int
配列を返します。このカウントは JDBC ドライバーによって報告されます。カウントが利用できない場合、JDBC ドライバーは -2
の値を返します。
このようなシナリオでは、基になる 6.1.2 以降、Spring は PostgreSQL および MS SQL Server のデフォルトの あるいは、対応する JDBC 型を明示的に指定することも検討できます。これには、 |
複数のバッチを使用したバッチ操作
前述のバッチ更新の例では、非常に大きいバッチを処理するため、いくつかの小さなバッチに分割します。batchUpdate
メソッドを複数回呼び出すことで、前述のメソッドでこれを行うことができますが、より便利なメソッドがあります。このメソッドは、SQL ステートメントに加えて、パラメーターを含むオブジェクトの Collection
、各バッチに対して行う更新の数、準備済みステートメントのパラメーターの値を設定する ParameterizedPreparedStatementSetter
を取ります。フレームワークは提供された値をループし、更新呼び出しを指定されたサイズのバッチに分割します。
次の例は、バッチサイズ 100 を使用するバッチ更新を示しています。
Java
Kotlin
public class JdbcActorDao implements ActorDao {
private JdbcTemplate jdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
public int[][] batchUpdate(final Collection<Actor> actors) {
int[][] updateCounts = jdbcTemplate.batchUpdate(
"update t_actor set first_name = ?, last_name = ? where id = ?",
actors,
100,
(PreparedStatement ps, Actor actor) -> {
ps.setString(1, actor.getFirstName());
ps.setString(2, actor.getLastName());
ps.setLong(3, actor.getId().longValue());
});
return updateCounts;
}
// ... additional methods
}
class JdbcActorDao(dataSource: DataSource) : ActorDao {
private val jdbcTemplate = JdbcTemplate(dataSource)
fun batchUpdate(actors: List<Actor>): Array<IntArray> {
return jdbcTemplate.batchUpdate(
"update t_actor set first_name = ?, last_name = ? where id = ?",
actors, 100) { ps, argument ->
ps.setString(1, argument.firstName)
ps.setString(2, argument.lastName)
ps.setLong(3, argument.id)
}
}
// ... additional methods
}
この呼び出しのバッチ更新メソッドは、各バッチの配列エントリと、各更新の影響を受ける行数の配列を含む int
配列の配列を返します。最上位の配列の長さは実行されたバッチの数を示し、第 2 レベルの配列の長さはそのバッチの更新の数を示します。各バッチの更新の数は、提供された更新オブジェクトの総数に応じて、すべてのバッチに提供されたバッチサイズ(最後のバッチより少ない場合を除く)である必要があります。各更新ステートメントの更新カウントは、JDBC ドライバーによって報告されたものです。カウントが使用できない場合、JDBC ドライバーは -2
の値を返します。