バッチ追加
DAO メソッドに @BatchInsert を付けて、バッチ追加操作を実行します。
@Dao
public interface EmployeeDao {
@BatchInsert
int[] insert(List<Employee> employees);
@BatchInsert
BatchResult<ImmutableEmployee> insert(List<ImmutableEmployee> employees);
}
デフォルトでは、INSERT ステートメントが自動生成されます。@BatchInsert アノテーション内の sqlFile プロパティに true を設定することで、任意の SQL ファイルをマッピングできます。
エンティティクラスにエンティティリスナーが指定されている場合、preInsert メソッドは追加操作を実行する前にそれぞれのエンティティに対して呼び出されます。同様に、追加操作が完了した後に、それぞれのエンティティに対して postInsert メソッドが呼び出されます。
戻り値
パラメータの要素がイミュータブルなエンティティクラスの場合、戻り値は org.seasar.doma.jdbc.BatchResult で、要素タイプとしてそのエンティティクラスが含まれていなければなりません。
上記の条件が満たされない場合、戻り値は int[] でなければならず、各要素は各挿入操作によって影響を受けた行数を表します。
自動生成されたSQLによるバッチ追加
パラメータの型は、エンティティ を要素とする java.lang.Iterable のサブタイプでなければなりません。指定できるパラメータは1つだけで、パラメータは null であってはなりません。返値の配列の要素数は Iterable の要素数と等しく、それぞれの配列要素は該当する更新操作によって影響を受ける行の数を表します。
ID
エンティティ の識別子に @GeneratedValue が付いている場合、識別子は自動的に生成され、設定されます。
注意点については IDの生成 を参照してください。
アプリケーションで自動生成された ID を使用しない場合は、ingoreGeneratedKeys フラグを有効にすることができます。このフラグによりパフォーマンスが向上する可能性があります。
@BatchInsert(ignoreGeneratedKeys = true)
int[] insert(List<Employee> entities);
バージョン番号
エンティティ に @Version が注釈されたプロパティがある場合、 そのプロパティに明示的に 0 以上の値が設定されていればその値を使用します。 もし設定されていないか、 0 未満の値が設定されていれば 1 を自動で設定します。
@BatchInsert のプロパティ
insertable
エンティティクラスに @Column が注釈されたプロパティが含まれており、その @Column の insertable プロパティが false に設定されている場合、プロパティは追加から除外されます。
exclude
@BatchInsert の exclude プロパティに指定されたプロパティは追加操作から除外されます。@Column の insertable プロパティが true に設定されていても、この要素にプロパティが指定されている場合は追加操作から除外されます。
@BatchInsert(exclude = {"name", "salary"})
int[] insert(List<Employee> employees);
include
@BatchInsert 注釈の include プロパティに指定されたプロパティのみが追加操作に含まれます。 @BatchInsert の include プロパティと exclude プロパティの両方にプロパティが指定されている場合、そのプロパティは挿入操作から除外されます。この要素でプロパティを指定していても、その @Column の insertable プロパティが false に設定されている場合、そのプロパティは追加操作から除外されます。
@BatchInsert(include = {"name", "salary"})
int[] insert(List<Employee> employees);
duplicateKeyType
このプロパティは、追加操作時に重複したキーを処理するための戦略を定義します。
次の3つの値のいずれかを取ることができます:
DuplicateKeyType.UPDATE: 重複したキーが見つかった場合、テーブル内の既存の行が更新されます。DuplicateKeyType.IGNORE: 重複したキーが見つかった場合、挿入操作は無視され、テーブルに変更は加えられません。DuplicateKeyType.EXCEPTION: 重複したキーが発生した場合、例外が投げられます。
@BatchInsert(duplicateKeyType = DuplicateKeyType.UPDATE)
int[] insert(List<Employee> employees);
duplicateKeys
このプロパティは、重複キーが存在するかどうかを判断するために使用するキーを表します。 重複キーが存在する場合、重複キーを処理するために duplicateKeyType ストラテジーを使用します。
@BatchInsert(duplicateKeyType = DuplicateKeyType.UPDATE, duplicateKeys = {"employeeNo"})
int[] insert(List<Employee> employees);
注釈
このプロパティは、duplicateKeyType ストラテジーが DuplicateKeyType.UPDATE または DuplicateKeyType.IGNORE のいずれかである場合にのみ使用されます。
注釈
MySQLの方言はこのプロパティを利用しません。
SQLファイルによるバッチ追加
SQLファイルによるバッチ追加を実行するには、@BatchInsert アノテーションの sqlFile プロパティを true に設定し、メソッドに対応するSQLファイルを用意します。
@BatchInsert(sqlFile = true)
int[] insert(List<Employee> employees);
@BatchInsert(sqlFile = true)
BatchResult<ImmutableEmployee> insert(List<ImmutableEmployee> employees);
パラメータの型は、エンティティ を要素とする java.lang.Iterable のサブタイプでなければなりません。指定できるパラメータは1つだけで、パラメータは null であってはなりません。返値の配列の要素数は Iterable の要素数と等しく、それぞれの配列要素は該当する更新操作によって影響を受ける行の数を表します。
エンティティリスナーが エンティティ に指定されている場合、SQLファイルを使用する際にそのメソッドは呼び出されません。
たとえば、上記のメソッドに対応するSQLは次のように記述します。
insert into employee (id, name, salary, version)
values (/* employees.id */0, /* employees.name */'hoge', /* employees.salary */100, /* employees.version */0)
SQL ファイル内のパラメータ名は、java.lang.Iterable サブタイプの要素を参照します。
SQLファイルから追加する際に、識別子およびバージョンの自動設定は行われません。
また、以下の @BatchInsert のプロパティは使用されません。
exclude
include
duplicateKeyType
duplicateKeys
一意制約違反
SQLファイルの使用にかかわらず、ユニーク制約違反が発生すると UniqueConstraintException がスローされます。
クエリタイムアウト
@BatchInsert アノテーションの queryTimeout プロパティにクエリタイムアウトの秒数を指定できます
@BatchInsert(queryTimeout = 10)
int[] insert(List<Employee> employees);
この指定は、SQLファイルの使用の有無に関係なく適用されます。queryTimeout プロパティが設定されていない場合、config クラスに指定されたクエリタイムアウトが使用されます。
バッチサイズ
バッチサイズは @BatchInsert アノテーション内の batchSize プロパティに指定できます。
@BatchInsert(batchSize = 10)
int[] insert(List<Employee> employees);
この指定は、SQL ファイルの使用の有無に関係なく適用されます。 batchSize プロパティに値を指定しない場合は、設定 クラスで指定されたバッチサイズが適用されます。
非常に大きなバッチでメモリ使用量を抑える
デフォルトでは、バッチに含まれるすべての PreparedSql がデータベースへの送信前にあらかじめ構築されます。数十万件規模の非常に大きなエンティティリストでは、batchSize を設定していてもヒープを使い切る可能性があります。batchSize は executeBatch() 1 回あたりに JDBC ドライバへ送る件数を決めるだけで、メモリ上に同時に存在する PreparedSql の数を制御するものではないからです。
ピーク時のメモリ使用量を抑えるには、Config で Query implementors をオーバーライドして ChunkedAutoBatchInsertQuery をオプトインで利用できます:
public class MyConfig implements Config {
private final QueryImplementors queryImplementors = new QueryImplementors() {
@Override
public <ENTITY> AutoBatchInsertQuery<ENTITY> createAutoBatchInsertQuery(
Method method, EntityType<ENTITY> entityType) {
return new ChunkedAutoBatchInsertQuery<>(entityType);
}
};
@Override
public QueryImplementors getQueryImplementors() {
return queryImplementors;
}
// ... other Config methods ...
}
このオーバーライドを適用すると、すべての @BatchInsert Dao メソッドはエンティティ 1 件分の準備済み SQL を順次構築し、バインド・JDBC バッチへの追加を行ったのち、次の SQL を構築する前に GC 対象になります。JDBC バッチのフラッシュ境界は引き続き batchSize に従いますが、メモリ上の SQL リスト自体は O(1) に保たれます。
これは完全なオプトインです。QueryImplementors を差し替えない限り、挙動は変わりません。
注釈
自動生成 SQL による batch update と batch delete にも chunked 版を利用できます。Batch update と Batch delete を参照してください。
SQLログの出力形式
@BatchInsert の sqlLog プロパティを使用して SQL ログ出力形式を指定できます。
@BatchInsert(sqlLog = SqlLogType.RAW)
int insert(Employee employee);
SqlLogType.RAW は、バインドパラメータでSQLをログに記録することを示します。