Java
MyBatis
java8
springframework

[MyBatis]大量データをマッピングするときはCursorを使おう

はじめに

この間仕事で、大量データを扱っていて、どうしてもレスポンスがでない処理を見直すことなりました。
全体的に、約4分くらいかかる処理でした。
この処理を見直すときに、MappperをListではなく、org.apache.ibatis.cursor.Cursorに修正しました。
その結果、レスポンスを早くすることができました。

せっかくなので、MyBatis3.4以降で追加されたCursorの使い方についてまとめていきます。

動作環境

  • Java 1.8.0_181
  • Spring FrameWork 4.3.16
  • MyBatis 3.4.5
  • mybatis-spring 1.3.1

※ mybatis-springだけ、プロジェクトによって必要/不必要が分かれると思います。

使い方

mapper.xmlは、Listで取得するときと特に変える必要はありません。
大量データを処理するので、fetchSizeでキャッシュのサイズを調整すると、さらに良いと思います。

mapper.xml
  <select id="getColumns" fetchSize="50000" resultType="java.lang.String">
    SELECT columns FROM table1
  </select>

Mapper.javaは、org.apache.ibatis.cursor.Cursorをインポートします。
<String>の部分は、SQLの戻り値に合わせてください。

Mapper.java
import org.apache.ibatis.cursor.Cursor;

@Mapper
public interface Mapper {
    public Cursor<String> getColumns();
}

Mapperから呼び出す部分です。
Cursor<T>型に呼び出し結果を格納する以外は、Listと同様です。

また、ループ処理で値を取り出す部分も、Listと同じように書くことができます。
サンプルでは、forEachとIteratorの2種類で書いています。

Service.java
@Service
public class Service {
    @Autowired
    private Mapper mapper;

    public void getMapper() {
        try (Cursor<String> columnsCorsor = mapper.getColumns()) {

            // ループ処理1
            columnsCorsor.forEach(s -> {
                System.out.println(s);
            });

            // ループ処理2(2回目のループはエラーするので注意!!)
            Iterator<String> iterator = columnsCorsor.iterator();
            while (iterator.hasNext()) {
                System.out.println(iterator.next());
            }

        } catch (Exception e) {
            e.printstacktrace();
        }
    }
}

注意しなければならないのは、Listで取得した時とは違い、Cursor内のデータを全て読み込んだタイミングで、closeされます。
そのため、1回でも全データを取り出したあとに、もう一度データを取り出しにいくと、下のようなエラーが出ます。

java.lang.IllegalStateException: Cannot open more than one iterator on a Cursor

基本的な使用方法の場合は、MyBatis側がクローズしてくれるので問題ないと思います。

しかし、途中でエラーが発生した場合、クローズしてくれる保証はありません。
なので、呼び出し元でtry~resource文かfinally句などで、しっかりとクローズしてあげましょう。

最後に

Cursorのおかげで、レスポンスを4分から2分まで短縮することができました。
(Cursor以外の部分も見直した結果ですが……)
大量データを処理する際は、ぜひCursorを活用してみましょう。

雑記

MyBatisって参考資料が少ないので、利用者は少ないのかなと思っています。
DBアクセス系のフレームワークは、何が主流なんでしょうか…?
MyBatisは生SQLをかけるし、良い感じにマッピングしてくれるので、個人的には気に入っているのですが…
アノテーションにたまにイラッとすることはありますが

参考文献

MyBatis – MyBatis 3 | Mapper XML ファイル
Cursor | mybatis
5.2. データベースアクセス(MyBatis3編) — TERASOLUNA Server Framework for Java (5.x) Development Guideline 5.0.1.RELEASE documentation