こんにちは。ASKULのほかほかごはんです。最近は商品データに関するバッチ開発を担当しています。
バッチ開発では、社内外へデータを連係する際に大量のデータをDBから取得し、csvなどのファイルに加工する機会が多くあります。 本記事では効率的にQuery結果をハンドリングする方法として、 MyBatisの ResultHandler と Cursor を紹介します。
なお、弊社ではSpring Boot上でMyBatisを利用しています。 本記事のサンプルコードもその前提で紹介させていただきます 🙇♂️
ResultHandler
ResultHandlerは検索結果を1件単位で処理する仕組みです。 検索結果が大量になった場合でも1件単位のメモリ消費で済むためメモリ枯渇による異常終了や性能劣化が起こる可能性を抑えることができます。
fun writeAllItems(path: Path) { MyItemFileWriter(path).use { writer -> myItemMapper.findAll( resultHandler = { resultContext -> writer.write(resultContext.resultObject) } ) } } @Mapper interface MyItemMapper { @Select( """ SELECT * FROM my_item """ ) @ResultType(MyItemDto::class) fun findAll( resultHandler: ResultHandler<MyItemDto> ) }
Cursor
CursorもResultHandlerと同じく大量データを効率的にハンドリングする仕組みです。 ResultHandlerよりも直感的にコードを記述できます。
個人的にはResultHandlerよりもCursorの方が好きです 😉
@Transactional(readOnly = true) fun writeAllItems(path: Path) { MyItemFileWriter(path).use { writer -> val myItemCursor = myItemMapper.findAll() myItemCursor.use { cursor -> cursor.forEach { writer.write(it) } } } } @Mapper interface MyItemMapper { @Select( """ SELECT * FROM my_item """ ) fun findAll(): Cursor<MyItemDto> }
Cursorの魅力はCollectionのコンテキストでコードを記述できるところです。 複数行をまとめて処理するなどのロジックを入れ込みやすくなります。
@Transactional(readOnly = true) fun writeAllItems(path: Path) { MyItemFileWriter(path).use { writer -> val myItemCursor = myItemMapper.findAll() val tmpDtoList = mutableListOf<MyItemDto>() myItemCursor.use { cursor -> cursor.forEach { // 例えば100件ごとに出力 tmpDtoList.add(it) if (tmpDtoList.size() >= 100) { writer.writeAll(tmpDtoList) tmpDtoList.clear() } } } if (tmpDtoList.isNotEmpty() writer.writeAll(tmpDtoList) } }
iteratorを使っての処理も可能です。
@Transactional(readOnly = true) fun writeAllItems(path: Path) { MyItemFileWriter(path).use { writer -> val myItemCursor = myItemMapper.findAll() myItemCursor.use { cursor -> val iterator = cursor.iterator() while (iterator.hasNext()) { val dto = iterator.next() // 例えば特定の行をskip if (dto.isFoo()) { continue } writer.write(dto) } } } }
まとめ
MyBatisで大量データを扱う際はResultHandlerかCursorを利用して省メモリかつ高速にデータハンドリングを行いましょう 🏎️