MyBatis | 検索結果を任意のJavaオブジェクトにマッピング | 関連オブジェクトのロードを遅延させる

ソースコード

sample_mapper.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="sample.mybatis">

  <resultMap type="sample.mybatis.Foo" id="fooResultMap">
    <id property="id" column="id" />
    <association property="bar" column="{key1=bar_key1,key2=bar_key2}" select="selectBar"
                 fetchType="lazy" /> <!-- fetchTypeをlazyに設定 -->
  </resultMap>

  <select id="selectFoo" resultMap="fooResultMap">
    select * from foo_table
  </select>

  <select id="selectBar" resultType="sample.mybatis.Bar">
    select *
      from bar_table
     where key1 = #{key1}
       and key2 = #{key2}
  </select>
</mapper>

Foo.java

package sample.mybatis;

public class Foo {
    private int id;
    public Bar bar;

    public void method() {
        System.out.println("Foo.method() is called");
    }
}

Main.java

package sample.mybatis;

import java.io.InputStream;
import java.util.List;

import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

public class Main {

    public static void main(String[] args) throws Exception {
        try (InputStream in = Main.class.getResourceAsStream("/mybatis-config.xml")) {
            SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);

            try (SqlSession session = factory.openSession()) {
                System.out.println("@selectList()");
                List<Foo> result = session.selectList("sample.mybatis.selectFoo");

                System.out.println("@result.get(0)");
                Foo foo = result.get(0);

                System.out.println("** foo.class = " + foo.getClass() + " **");

                System.out.println("@foo.bar 1");
                System.out.println("<< foo.bar = " + foo.bar + " >>");

                System.out.println("@foo.method()");
                foo.method();

                System.out.println("@foo.bar 2");
                System.out.println("<< foo.bar = " + foo.bar + " >>");
            }
        }
    }
}

実行結果

@selectList()
[DEBUG] s.m.selectFoo   - ==>  Preparing: select * from foo_table 
[DEBUG] s.m.selectFoo   - ==> Parameters: 
[DEBUG] s.m.selectFoo   - <==      Total: 3
@result.get(0)
** foo.class = class sample.mybatis.Foo_$$_jvst80f_0 **
@foo.bar 1
<< foo.bar = null >>
@foo.method()
[DEBUG] s.m.selectBar   - ==>  Preparing: select * from bar_table where key1 = ? and key2 = ? 
[DEBUG] s.m.selectBar   - ==> Parameters: 1(Integer), fuga(String)
[DEBUG] s.m.selectBar   - <==      Total: 1
Foo.method() is called
@foo.bar 2
<< foo.bar = Bar [key1=1, key2=fuga] >>

説明

  • <association>のfetchType属性にlazyを設定すると、関連オブジェクトのロードを遅延できる。
    • デフォルトは即時ロード(eager)である。
  • 遅延ロードが設定されたクラスのインスタンスは、MyBatisによって生成されたプロキシになる(class sample.mybatis.Foo _ $$ _ jvst80f_0)。
    • 遅延ロードでない場合は、通常のインスタンスが渡される。
  • 遅延ロードが実行されるタイミングは、プロキシ内のいずれかのメソッドが実行されたときのようである。
  • フィールドを読み取ったときではないため、メソッド実行前に参照するとnullになる。通常は直接見せるものではないと思うが、参考として覚えておく。

デフォルト設定を遅延ロードにしたい場合は、ルート設定でlazyLoadingEnabledを設定する方法がある。

mybatis-config.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
  <settings>
    <setting name="lazyLoadingEnabled" value="true"/> <!-- trueにする。 -->
  </settings>

  ...
</configuration>
  • この場合でも<association>タグのfetchTypeが優先されるため、特定のロードだけ即時ロードにしたい場合は設定を上書きできる。