Oracle は、SQLバインド変数の上限を超えると、
ORA-01745: ホスト/バインド変数名が無効です。
が発生する
Oracle のバインド変数の上限は、32768
PostgreSQL は、32767
MySQL はクエリの上限サイズで制限を見た方が良いみたい
show variables like 'max_allowed_packet'
mybatis で、バルクインサートをする場合に、もしもこの上限を超えるのであれば、
分割して実行するしかない。
そんな時、List要素を任意の数で分割したリストで処理したい。
以下のようなメソッドが汎用的であろう。
public static <T> List<List<T>> chunklist(List<T> list, int size) { return IntStream.range(0, (int)Math.ceil((double)list.size() / (double)size)) .boxed() .map(i -> list.stream().skip((long)i * size).limit(size).collect(Collectors.toList())) .collect(Collectors.toList()); }
1回のSQLのバインド数×リストサイズ > 上限値の場合、
バルクインサートにおける利用では、
CEIL( 1回のSQLのバインド数×リストサイズ / 上限値 )
の数を size に指定すれば良い。
もちろん。バルクインサートでない1回のSQLが、
バインド変数の数 > 上限値 だったらどうしようもない。
Java9 以降なら、
public static <T> List<List<T>> chunklist(List<T> list, int size) { return Stream.iterate(0, n -> n + size) .limit((list.size() + size - 1) / size) .map(n -> list.subList(n, Math.min(n + size, list.size()))) .collect(Collectors.toList()); }
メソッドにしないまでも、、
Throwable な Consumer<T> を使って、以下でも良い。
int size = 1200; IntStream.range(0, (int)Math.ceil((double)list.size() / (double)size)) .boxed() .map(i -> list.stream().skip((long)i * size) .limit(size) .collect(Collectors.toList())) .forEach(ThrowableConsumer.of(e->{ // TODO sqlSesssion.insert(sqlid, e); }));
Java9 以降~
int size = 1200; Stream.iterate(0, n->n + size) .limit((list.size() + size - 1) / size) .map(n -> list.subList(n, Math.min(n + size, list.size()))) .forEach(ThrowableConsumer.of(e->{ // TODO sqlSesssion.insert(sqlid, e); }));