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가 우선되기 때문에 특정 로드만 즉시 로드하고 싶은 경우는 설정을 무시할 수 있다.