LoginSignup
29
32

More than 5 years have passed since last update.

MyBatis 3.4で追加されたCursorの使い方

Last updated at Posted at 2016-04-29

今回はMyBaitsのCursor機能の使い方について紹介します。
Cursor機能はMyBatis 3.4から追加された新機能で、大量データを効率的+直感的にJavaBeanにマッピングするために追加されました。

動作検証バージョン

  • MyBatis 3.4.0
  • MyBatis Spring 1.3.0
  • Spring Framework 4.2.5.RELEASE
  • Spring Boot 1.3.3.RELEASE

3.3.xまでの実装方法のおさらい

MyBatis 3.3までは、大量データを扱う時はorg.apache.ibatis.session.ResultHandlerを使っていました。

以下は、ResultHandlerを使った実装例です。

src/main/java/com/example/mapper/TodoMapper.java
public interface TodoMapper {
    @Select("SELECT id, title, details, finished FROM todo ORDER BY id")
    @ResultType(Todo.class)
    void collect(ResultHandler<Todo> handler);
}

Java SE 8でサポートされた関数型インタフェース + ラムダ式を活用することでシンプルに利用できますが、Java SE 7ユーザーにとっては多少煩雑な書き方が求められます。

  • Java SE 8での利用例
todoMapper.collect(context -> {
    Todo todo = context.getResultObject();
    System.out.println("ID       : " + todo.getId());
    System.out.println("TITLE    : " + todo.getTitle());
    System.out.println("DETAILS  : " + todo.getDetails());
    System.out.println("FINISHED : " + todo.isFinished());
});
  • Java SE 7での利用例 (ちょっと煩雑・・・)
todoMapper.collect(new ResultHandler<Todo>() {
    @Override
    public void handleResult(ResultContext<? extends Todo> context) {
        Todo todo = context.getResultObject();
        System.out.println("ID       : " + todo.getId());
        System.out.println("TITLE    : " + todo.getTitle());
        System.out.println("DETAILS  : " + todo.getDetails());
        System.out.println("FINISHED : " + todo.isFinished());
    }
});

なお、MyBatisをてっとり早く試したい方は、MyBatis-Spring-Bootを使うことをお勧めします!!
MyBatis-Spring-Bootの使い方については、こちらの記事をご覧ください。本記事のサンプルコードもMyBatis-Spring-Bootを使って動作検証を行っています。

MyBatisのCursor機能の利用

MyBatisのCursor機能を使ってみましょう。

Warning: Cursor機能の制約事項(バグ)について
3.4.0には以下のバグがありましたが、これらのバグはMyBatis 3.4.1で解消されました!!

Cursor機能を使う場合は、Mapperメソッドの返り値をorg.apache.ibatis.cursor.Cursorにするだけです。

src/main/java/com/example/mapper/TodoMapper.java
@Mapper
public interface TodoMapper {
    Cursor<Todo> selectCursor();
}
src/main/resource/com/example/mapper/TodoMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.TodoMapper">
    <select id="selectCursor" resultType="com.example.domain.Todo">
        SELECT id, title, details, finished FROM todo ORDER BY id
    </select>
</mapper>

MyBatis 3.4.1がリリースされたため、XMLファイルだけではなく、以下のようにアノテーションにもSQLを指定することができるようになりました :smile:

src/main/java/com/example/mapper/TodoMapper.java
@Mapper
public interface TodoMapper {
    @Select("SELECT id, title, details, finished FROM todo ORDER BY id")
    Cursor<Todo> selectCursor();
}

Cursorインタフェースはjava.io.Closable(java.lang.AutoCloseable)とjava.util.Iterableを継承しているため、以下のように利用することができます。

  • Cursorの利用例
try (Cursor<Todo> todos = todoMapper.selectCursor()) { // try-with-resources文でCursorを確実にクローズする
    for (Todo todo : todos) { // 拡張for文でカーソルをフェッチする
        System.out.println("ID       : " + todo.getId());
        System.out.println("TITLE    : " + todo.getTitle());
        System.out.println("DETAILS  : " + todo.getDetails());
        System.out.println("FINISHED : " + todo.isFinished());
    }
}

Note: Cursorのクローズについて
カーソル上にあるすべてのデータを読み込んだタイミングでMyBatis側がCursorをクローズしてくれますが、途中でエラーが発生した場合はクローズの保証はありません。そのため、Cursorを利用する側がCursorのクローズを保証する必要があります。

RowBoundsを利用したCursorの範囲検索を利用する際の注意点

MyBatis 3.4.0ではバグで範囲検索を利用することができませんが、この問題はMyBatis 3.4.1で解消されました。
バグが修正されても、実はCursorの範囲検索には潜在的な問題があります。
潜在的な問題とは、取得開始位置に移動(スキップ)する仕組みにあります。
現在の実装では、開始位置までの不要データの読み込み+読み込んだデータをJavaBeanへのマッピングする処理も実行されるため、効率的なスキップ処理とは言えません。これは、1:1や1:Nの関係をもつネストしたプロパティへのマッピングをサポートした上でのスキップ処理を実現しているためです。

結論としては・・・・

  • Cursorの母体がたいした件数にならない(スキップ処理にかかる処理コストが問題にならない)なら、RowBoundsを使ってもOK
  • 0件目からN件目までのCursorを取得したい場合は RowBoundsを使ってもOK
  • N件目からY件目までのCursorを取得したい場合は、SQLでN件目までのデータを取得しないようにした方がよい (Y件目の制御はSQLでもいいし、RowBoundsどちらでもいい)

かな〜。

MyBatis-Springでのサポート

MyBatis 1.3からCursor機能がサポートされており、Spring Batch用にorg.mybatis.spring.batch.MyBatisCursorItemReaderというクラスも追加されています。

MyBatis-Spring-Bootでのサポート

MyBatis-Spring-Bootの1.1系(投稿時点での最新バージョン)では、MyBatis 3.4 + MyBatis-Spring 1.3がサポートされているため、Cursor機能も利用できます。

まとめ

個人的にはいい感じのインターフェースだな〜と思っています :smile: が、
MyBatis 3.4.0だとバグに伴う制約があるので、実際に使うのはMyBatis 3.4.1のリリースを待ってからの方が無難な気がします。

2016/6/26に3.4.1がリリースされました!!

29
32
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
29
32