반응형

기본

데이터 테이블

test_table

idvalue
1fizz
2buzz

소스 코드

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">
  <select id="selectTest" resultType="sample.mybatis.TestTable">
    select * from test_table
  </select>
</mapper>

TestTable.java

package sample.mybatis;

public class TestTable {

    private int id;
    private String value;

    public void setValue(String value) {
        System.out.println("setValue(" + value + ")");
        this.value = value;
    }

    @Override
    public String toString() {
        return "TestTable [id=" + id + ", value=" + value + "]";
    }
}

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()) {
                List<TestTable> result = session.selectList("sample.mybatis.selectTest");
                System.out.println(result);
            }
        }
    }
}

실행 결과

[DEBUG] s.m.selectTest  - ==>  Preparing: select * from test_table 
[DEBUG] s.m.selectTest  - ==> Parameters: 
setValue(fizz)
setValue(buzz)
[DEBUG] s.m.selectTest  - <==      Total: 2
[TestTable [id=1, value=fizz], TestTable [id=2, value=buzz]]

설명

  • resultType에 검색 결과를 매핑하고 싶은 Java 클래스를 지정한다.
  • 컬럼 이름과 일치하는 필드에 값이 설정된다.
  • 값 설정은 Setter 메소드가 있으면 그것을 통하고, 없으면 필드에 직접 이루어 진다.



snake_case와 CamelCase 매핑 자동화

데이터 테이블

test_table

idvalue
1hoge
2fuga
3piyo

소스 코드

TestTable.java

package sample.mybatis;

public class TestTable {
    private int id;
    private String testValue;

    @Override
    public String toString() {
        return "TestTable [id=" + id + ", testValue=" + testValue + "]";
    }
}

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="mapUnderscoreToCamelCase" value="true"/>
  </settings>

  ...
</configuration>

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">

  <select id="selectTest" resultType="sample.mybatis.TestTable">
    select * from test_table
  </select>
</mapper>

selectTest의 실행 결과

TestTable [id=1, testValue=hoge]
TestTable [id=2, testValue=fuga]
TestTable [id=3, testValue=piyo]

설명

  • mapUnderscoreToCamelCase을 true로 설정을 하면, snake_case와 CamelCase의 자동 변환을 해준다.
  • 기본값은 false로 되어있다.

클래스로 매핑 정의

코드

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 id="testTableResultMap" type="sample.mybatis.TestTable">
    <id property="id" column="id" />
    <result property="value" column="value" />
  </resultMap>

  <select id="selectTest" resultMap="testTableResultMap">
    select * from test_table
  </select>
</mapper>

설명

  • <resultMap> 태그를 사용하여 검색 결과와 Java 클래스 간의 매핑을 정의할 수 있다.
  • <id> 태그로 식별자 속성을 정의한다.
    • 식별자 속성은 인스턴스를 식별하는데 사용하는 속성을 말한다.
    • 식별자 속성을 지정해 두는 것으로, 캐시 및 JOIN 매핑 때 성능이 향상 되는거 같다.
    • property 속성에 Java 측의 속성(필드) 이름을 지정한다.
    • column 속성에 데이터베이스 측의 열 이름을 지정한다.
  • <result> 태그로 각 열의 매핑을 정의한다.
    • 이쪽도 property와 column 이름을 매핑한다.
  • <resultMap>의 id 속성으로 매핑을 고유하게 식별하는 이름을 정의한다 (testTableResultMap).
    • 이 이름을 <select> 태그의 resultMap 속성에 지정한다.



단일 오브젝트 맵핑

기본

데이터 테이블

test_table

idvaluenumber
1hoge100
2fuga200
3piyo300

소스 코드

TestTable.java

package sample.mybatis;

public class TestTable {

    private int id;
    private String string;
    private Embedded embedded;

    @Override
    public String toString() {
        return "TestTable [id=" + id + ", string=" + string + ", embedded=" + embedded + "]";
    }
}

Embedded.java

package sample.mybatis;

public class Embedded {

    private int number;

    @Override
    public String toString() {
        return "Embedded [number=" + number + "]";
    }
}

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.TestTable" id="testTableResultMap">
    <id property="id" column="id" />
    <result property="string" column="string" />
    <association property="embedded" javaType="sample.mybatis.Embedded">
      <result property="number" column="number" />
    </association>
  </resultMap>

  <select id="selectTest" resultMap="testTableResultMap">
    select * from test_table
  </select>
</mapper>

Main.java

package sample.mybatis;

import java.io.InputStream;

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()) {
                session
                    .selectList("sample.mybatis.selectTest")
                    .forEach(System.out::println);
            }
        }
    }
}

실행 결과

TestTable [id=1, string=hoge, embedded=Embedded [number=100]]
TestTable [id=2, string=fuga, embedded=Embedded [number=200]]
TestTable [id=3, string=piyo, embedded=Embedded [number=300]]

설명

  • <association> 태그으로 내장된 클래스를 매핑할 수 있다.
  • javaType 속성에 포함된 클래스를 지정한다 (별칭 가능).
  • 자식 요소에 포함된 클래스의 매핑을 정의한다.

포함된 클래스의 정의를 별도 정의로부터

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.TestTable" id="testTableResultMap">
    <id property="id" column="id" />
    <result property="string" column="string" />

    <association property="embedded" resultMap="embeddedResultMap" />
  </resultMap>

  <resultMap type="sample.mybatis.Embedded" id="embeddedResultMap">
    <result property="number" column="number" />
  </resultMap>

  <select id="selectTest" resultMap="testTableResultMap">
    select * from test_table
  </select>
</mapper>
  • <resultMap>에 별도로 정의할 수 있다.


기본

데이터 테이블

test_table

idvaluenumber
1hoge100
2fuga200
3piyo300

소스 코드

TestTable.java

package sample.mybatis;

public class TestTable {

    private int id;
    private String string;
    private Embedded embedded;

    @Override
    public String toString() {
        return "TestTable [id=" + id + ", string=" + string + ", embedded=" + embedded + "]";
    }
}

Embedded.java

package sample.mybatis;

public class Embedded {

    private int number;

    @Override
    public String toString() {
        return "Embedded [number=" + number + "]";
    }
}

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.TestTable" id="testTableResultMap">
    <id property="id" column="id" />
    <result property="string" column="string" />
    <association property="embedded" javaType="sample.mybatis.Embedded">
      <result property="number" column="number" />
    </association>
  </resultMap>

  <select id="selectTest" resultMap="testTableResultMap">
    select * from test_table
  </select>
</mapper>

Main.java

package sample.mybatis;

import java.io.InputStream;

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()) {
                session
                    .selectList("sample.mybatis.selectTest")
                    .forEach(System.out::println);
            }
        }
    }
}

실행 결과

TestTable [id=1, string=hoge, embedded=Embedded [number=100]]
TestTable [id=2, string=fuga, embedded=Embedded [number=200]]
TestTable [id=3, string=piyo, embedded=Embedded [number=300]]

설명

  • <association> 태그으로 내장된 클래스를 매핑할 수 있다.
  • javaType 속성에 포함된 클래스를 지정한다 (별칭 가능).
  • 자식 요소에 포함된 클래스의 매핑을 정의한다.

포함된 클래스의 정의를 별도 정의로부터

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.TestTable" id="testTableResultMap">
    <id property="id" column="id" />
    <result property="string" column="string" />

    <association property="embedded" resultMap="embeddedResultMap" />
  </resultMap>

  <resultMap type="sample.mybatis.Embedded" id="embeddedResultMap">
    <result property="number" column="number" />
  </resultMap>

  <select id="selectTest" resultMap="testTableResultMap">
    select * from test_table
  </select>
</mapper>
  • <resultMap>에 별도로 정의할 수 있다.


콜렉션 매핑

기본

DB 테이블

foo_table

id
1
2
3

bar_table

key1foo_id
11
21
31
42
52
63

소소 코드

Foo.java

package sample.mybatis;

import java.util.List;

public class Foo {
    private int id;
    private List<Bar> barList;

    @Override
    public String toString() {
        return "Foo [id=" + id + ", barList=" + barList + "]";
    }
}

Bar.java

package sample.mybatis;

public class Bar {
    private int id;

    @Override
    public String toString() {
        return "Bar [id=" + id + "]";
    }
}

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" />
    <!-- collection 태그를 사용한다. -->
    <collection property="barList" ofType="sample.mybatis.Bar">
      <id property="id" column="bar_id" />
    </collection>
  </resultMap>

  <select id="selectFoo" resultMap="fooResultMap">
    select foo.id
          ,bar.id bar_id
      from foo_table foo
          ,bar_table bar
     where bar.foo_id = foo.id
  </select>
</mapper>

selectFoo의 실행 결과

[DEBUG] s.m.selectFoo   - ==>  Preparing: select foo.id ,bar.id bar_id from foo_table foo ,bar_table bar where bar.foo_id = foo.id 
[DEBUG] s.m.selectFoo   - ==> Parameters: 
[DEBUG] s.m.selectFoo   - <==      Total: 6
Foo [id=1, barList=[Bar [id=1], Bar [id=2], Bar [id=3]]]
Foo [id=2, barList=[Bar [id=4], Bar [id=5]]]
Foo [id=3, barList=[Bar [id=6]]]

설명

  • 컬렉션을 매핑하려면 <collection> 태그를 사용한다.
  • 각 요소의 형태는 ofType 속성에서 지정한다 (별칭 가능).
  • 그 외에는 <association> 태그의 때와 동일한 방식으로 설정한다.



컬럼 이름에 접두어(prefix)를 지정하기

데이터 테이블

foo_table

idbar_id
13
22
31

bar_table

id
1
2
3

소스 코드

Foo.java

package sample.mybatis;

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

    @Override
    public String toString() {
        return "Foo [id=" + id + ", bar=" + bar + "]";
    }
}

Bar.java

package sample.mybatis;

public class Bar {
    private int id;

    @Override
    public String toString() {
        return "Bar [id=" + id + "]";
    }
}

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" />
    <!-- columnPrefix에서 공용하는 접두어를 지정한다. -->
    <association property="bar" columnPrefix="bar_" resultMap="barResultMap" />
  </resultMap>

  <resultMap type="sample.mybatis.Bar" id="barResultMap">
    <id property="id" column="id" />
  </resultMap>

  <select id="selectFoo" resultMap="fooResultMap">
    select foo.id
          ,bar.id bar_id -- "bar_"를 접두어로 지정
      from foo_table foo
          ,bar_table bar
     where bar.id = foo.bar_id
  order by foo.id asc
  </select>
</mapper>

selectFoo의 실행 결과

Foo [id=1, bar=Bar [id=3]]
Foo [id=2, bar=Bar [id=2]]
Foo [id=3, bar=Bar [id=1]]

설명

  • JOIN을 할 때 다른 테이블의 동명 컬럼과 구별하기 위해, 보통 접두사(bar_)을 붙일 것이다.
  • 그런 경우에도 resultMap을 사용할 수 있도록, columnPrefix라는 속성이 포함되어 있다.
  • columnPrefix을 지정하면 column의 설정 값에 접두사를 붙인 명칭으로 매핑이 이루어지게 된다.


SELECT를 나누어 실행하기

단일 오브젝트 맵핑에서 SELECT를 나누어 실행

소스 코드

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="bar_id" select="selectBar" />
  </resultMap>

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

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

selectFoo의 실행 결과

[DEBUG] s.m.selectFoo   - ==>  Preparing: select * from foo_table 
[DEBUG] s.m.selectFoo   - ==> Parameters: 
[DEBUG] s.m.selectBar   - ====>  Preparing: select * from bar_table where id = ? 
[DEBUG] s.m.selectBar   - ====> Parameters: 1(Integer)
[DEBUG] s.m.selectBar   - <====      Total: 1
[DEBUG] s.m.selectBar   - ====>  Preparing: select * from bar_table where id = ? 
[DEBUG] s.m.selectBar   - ====> Parameters: 2(Integer)
[DEBUG] s.m.selectBar   - <====      Total: 1
[DEBUG] s.m.selectBar   - ====>  Preparing: select * from bar_table where id = ? 
[DEBUG] s.m.selectBar   - ====> Parameters: 3(Integer)
[DEBUG] s.m.selectBar   - <====      Total: 1
[DEBUG] s.m.selectFoo   - <==      Total: 3
Foo [id=3, bar=Bar [id=1]]
Foo [id=2, bar=Bar [id=2]]
Foo [id=1, bar=Bar [id=3]]

설명

  • foo_table을 검색한 후에 각 열에 대해 bar_table 검색을 별도로 수행하고 있다.
  • <association> 태그의 select 속성에 <select> 태그의 Statement ID를 지정하는 것으로, 이런 검색을 할 수 있다.
  • column 속성에는 자식 테이블의 JOIN을 사용하는 컬럼을 지정한다.

JOIN 키가 여러 개 있는 경우

DB 테이블

foo_table

idbar_key1bar_key2
12hoge
21fuga
31hoge

bar_table

key1key2
1fuga
1hoge
2hoge

소스 코드

Bar.java

package sample.mybatis;

public class Bar {
    private int key1;
    private String key2;

    @Override
    public String toString() {
        return "Bar [key1=" + key1 + ", key2=" + key2 + "]";
    }
}

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" />
  </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>

selectFoo의 실행 결과

[DEBUG] s.m.selectFoo   - ==>  Preparing: select * from foo_table 
[DEBUG] s.m.selectFoo   - ==> Parameters: 
[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
[DEBUG] s.m.selectBar   - ====>  Preparing: select * from bar_table where key1 = ? and key2 = ? 
[DEBUG] s.m.selectBar   - ====> Parameters: 1(Integer), hoge(String)
[DEBUG] s.m.selectBar   - <====      Total: 1
[DEBUG] s.m.selectBar   - ====>  Preparing: select * from bar_table where key1 = ? and key2 = ? 
[DEBUG] s.m.selectBar   - ====> Parameters: 2(Integer), hoge(String)
[DEBUG] s.m.selectBar   - <====      Total: 1
[DEBUG] s.m.selectFoo   - <==      Total: 3
Foo [id=2, bar=Bar [key1=1, key2=fuga]]
Foo [id=3, bar=Bar [key1=1, key2=hoge]]
Foo [id=1, bar=Bar [key1=2, key2=hoge]]

설명

  • JOIN할 때에 열이 복수 존재하는 경우는 {prop1=column1, prop2=column2} 형태로 column 속성을 지정한다.

콜렉션 맵핑에서 SELECT를 나누어 실행

소스 코드

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" />
    <collection property="barList" column="id" select="selectBar" />
  </resultMap>

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

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

selectFoo의 실행 결과

[DEBUG] s.m.selectFoo   - ==>  Preparing: select * from foo_table 
[DEBUG] s.m.selectFoo   - ==> Parameters: 
[DEBUG] s.m.selectBar   - ====>  Preparing: select * from bar_table where foo_id = ? 
[DEBUG] s.m.selectBar   - ====> Parameters: 1(Integer)
[DEBUG] s.m.selectBar   - <====      Total: 3
[DEBUG] s.m.selectBar   - ====>  Preparing: select * from bar_table where foo_id = ? 
[DEBUG] s.m.selectBar   - ====> Parameters: 2(Integer)
[DEBUG] s.m.selectBar   - <====      Total: 2
[DEBUG] s.m.selectBar   - ====>  Preparing: select * from bar_table where foo_id = ? 
[DEBUG] s.m.selectBar   - ====> Parameters: 3(Integer)
[DEBUG] s.m.selectBar   - <====      Total: 1
[DEBUG] s.m.selectFoo   - <==      Total: 3
Foo [id=1, barList=[Bar [id=1], Bar [id=2], Bar [id=3]]]
Foo [id=2, barList=[Bar [id=4], Bar [id=5]]]
Foo [id=3, barList=[Bar [id=6]]]

설명

  • <association> 때와 같은 방식으로 설정한다.
  • 지연 로드 역시 가능하다.


관련 개체 로드를 지연시키기

소스 코드

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


조건에 따라 다른 클래스에 매핑

주로 상속 관계에 있는 클래스를 매핑할 때 사용하는 방법이다.

DB 테이블

base_table

idcommon_valuetypehoge_valuefuga_valuepiyo_value
1common11hoge1fuga1piyo1
2common22hoge2fuga2piyo2
3common33hoge3fuga3piyo3

소스 코드

Base.java

package sample.mybatis;

public class Base {
    protected int id;
    protected String commonValue;

    @Override
    public String toString() {
        return "Base [id=" + id + ", commonValue=" + commonValue + "]";
    }
}

Hoge.java

package sample.mybatis;

public class Hoge extends Base {
    private String value;

    @Override
    public String toString() {
        return "Hoge [value=" + value + ", id=" + id + ", commonValue=" + commonValue + "]";
    }
}

Fuga.java

package sample.mybatis;

public class Fuga extends Base {
    private String value;

    @Override
    public String toString() {
        return "Fuga [value=" + value + ", id=" + id + ", commonValue=" + commonValue + "]";
    }
}

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.Base" id="baseResultMap">
    <id property="id" column="id" />
    <result property="commonValue" column="common_value" />

    <discriminator javaType="int" column="type">
      <case value="1" resultType="sample.mybatis.Hoge">
        <result property="value" column="hoge_value" />
      </case>
      <case value="2" resultType="sample.mybatis.Fuga">
        <result property="value" column="fuga_value" />
      </case>
    </discriminator>
  </resultMap>

  <select id="selectBase" resultMap="baseResultMap">
    select * from base_table
  </select>
</mapper>

selectBase의 실행 결과

Hoge [value=hoge1, id=1, commonValue=common1]
Fuga [value=fuga2, id=2, commonValue=common2]
Base [id=3, commonValue=common3]

설명

  • <discriminator> 태그를 사용하는 것으로, 조건에 따라 다른 클래스에 매핑시킬 수 있게 된다.
    • column 속성에 조건 값을 가진 컬럼을 지정한다.
  • <case> 태그에서 조건 값에 대해 어떤 매핑을 실시 하는지를 정의한다.
    • 여기에서는 1의 경우 Hoge 클래스, 2의 경우 Fuga 클래스에 매핑하도록 정의하고 있다.
  • <case> 식에 존재하지 않는 조건 값의 경우는 원래의 <resultMap> 태그로 지정했던 type에 매핑된다.

케이스마다 매핑을 외부 설정으로 하기

소스 코드

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.Base" id="baseResultMap">
    <id property="id" column="id" />
    <result property="commonValue" column="common_value" />

    <discriminator javaType="int" column="type">
      <case value="1" resultMap="hogeResultMap" />
      <case value="2" resultMap="fugaResultMap" />
    </discriminator>
  </resultMap>

  <resultMap type="sample.mybatis.Hoge" id="hogeResultMap">
    <result property="value" column="hoge_value" />
  </resultMap>

  <resultMap type="sample.mybatis.Fuga" id="fugaResultMap">
    <result property="value" column="fuga_value" />
    <result property="commonValue" column="common_value" />
  </resultMap>

  <select id="selectBase" resultMap="baseResultMap">
    select * from base_table
  </select>
</mapper>

실행 결과

Hoge [value=hoge1, id=1, commonValue=null]
Fuga [value=fuga2, id=2, commonValue=common2]
Base [id=3, commonValue=common3]

설명

  • <case> 당 정의는 <resultMap>에서 추출할 수 있다.
  • 그러나 그 경우 공통 부분으로 정의하고 컬럼은 <id>이외에는 매핑되지 않기 때문에, 개별 <resultMap>에서 정의하지 않으면 안된다.
    • hogeResultMap는 commonValue을 정의하고 있지 않기 때문에 null로 되어 있다.
  • 일일이 개별 <resultMap>에서 공통 항목을 정의하고 고치는 것이 힘든 경우 extends 속성을 사용하는 방법이 준비되어 있다.

extends 속성으로 공통 부분을 상속

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.Base" id="baseResultMap">
    <id property="id" column="id" />
    <result property="commonValue" column="common_value" />

    <discriminator javaType="int" column="type">
      <case value="1" resultMap="hogeResultMap" />
      <case value="2" resultMap="fugaResultMap" />
    </discriminator>
  </resultMap>
                                                           <!-- extends으로 baseResultMap을 상속 -->
  <resultMap type="sample.mybatis.Hoge" id="hogeResultMap" extends="baseResultMap">
    <result property="value" column="hoge_value" />
  </resultMap>

  <resultMap type="sample.mybatis.Fuga" id="fugaResultMap" extends="baseResultMap">
    <result property="value" column="fuga_value" />
  </resultMap>

  <select id="selectBase" resultMap="baseResultMap">
    select * from base_table
  </select>
</mapper>

실행 결과

Hoge [value=hoge1, id=1, commonValue=common1]
Fuga [value=fuga2, id=2, commonValue=common2]
Base [id=3, commonValue=common3]



캐시

소스 코드

Main.java

package sample.mybatis;

import java.io.InputStream;

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 session1 = factory.openSession();
                 SqlSession session2 = factory.openSession();) {

                TestTable testTable = selectAndPrintln("session1", session1);
                testTable.setValue("update");

                selectAndPrintln("session1", session1);

                selectAndPrintln("session2", session2);
            }
        }
    }

    private static TestTable selectAndPrintln(String tag, SqlSession session) {
        TestTable result = session.selectOne("sample.mybatis.selectTest");
        System.out.printf("<<%s>> %s%n", tag, result);
        return result;
    }
}

실행 결과

[DEBUG] s.m.selectTest  - ==>  Preparing: select * from test_table where id=1 
[DEBUG] s.m.selectTest  - ==> Parameters: 
[DEBUG] s.m.selectTest  - <==      Total: 1
<<session1>> TestTable [id=1, value=hoge]
<<session1>> TestTable [id=1, value=update]
[DEBUG] s.m.selectTest  - ==>  Preparing: select * from test_table where id=1 
[DEBUG] s.m.selectTest  - ==> Parameters: 
[DEBUG] s.m.selectTest  - <==      Total: 1
<<session2>> TestTable [id=1, value=hoge]

설명

  • 검색 결과는 캐시된다.
  • 동일한 검색을 다시 실행하면 캐시된 객체가 반환된다.
  • 캐시는 세션마다 저장되므로 다른 세션에서 변경의 영향을 받지 않는다.
  • 기본값은 다음과 같은 동작을 하는 것 같다.
    • 1024 개체를 캐시한다.
    • 가장 사용되지 않은 캐시에서 제거되어 간다.
    • 시간 경과에 제거되지 않는다.
    • insert, update 및 delete가 실행되면 캐시는 지워진다.
  • 이 기본 동작은 설정 파일에 덮어 쓸 수 있다.
  • 설정의 자세한 내용은 여기를 참조하길 바란다.


반응형

'MyBatis' 카테고리의 다른 글

[MyBatis] 변경 UPDATE  (0) 2017.12.31
[MyBatis] 등록 INSERT  (1) 2017.12.31
[MyBatis] 검색 결과를 임의의 Java 오브젝트에 매핑  (0) 2017.12.31
[MyBatis] 검색 SELECT  (0) 2017.12.31
[MyBatis] 실행되는 SQL을 로그에 출력  (0) 2017.12.31
[MyBatis] 설정 파일  (0) 2017.12.31

+ Recent posts