[MyBatis] MyBatis의 Association과 Collection
MyBatis에서 Domain 객체와 쿼리를 맵핑을 하다가 막혀서 이 부분을 공부하게 되었다. flat하게 데이터를 주는 쿼리와 다르게, 계층형태로 데이터를 전달해야하다 보면 편하게 맵핑할 방법을 찾게 된다. 이걸 Association과 Collection으로 해결할 수 있다.
세 개의 도메인으로 예를 들어보자.
Artist - Album - Song
Artist는 여러개의 앨범을 낼 수 있다. Album 안에는 여러 개의 Song이 있다. 즉, Artist와 Album은 1:N의 관계이다. Album과 Song 역시 1:N 관계이다.
만약 특정한 seq번호가 3인 앨범의 정보와 아티스트, 앨범 A에 수록된 곡을 알고 싶다면 어떻게 해야할까?
기존의 나
- select로 seq번호가 3인 앨범 정보를 가져온다.
- Artist는 seq번호로 JOIN을 이용해 가져온다.
- Song은 seq번호로 가져오면 되는데, 여러 개의 결과값이 있다.
- Dao에서 Song을 가져오는 로직을 만든다.
- 그럼 List로 받아서 비즈니스 로직을 작성한다.
- 데이터 한번 가져오는데 DB 접근은 두번 한다.
- 코드는 지저분하다.
뽀글 뽀글....
Association과 Collection
Association과 Collection을 이용하면 기존의 나보다 훨씬 편하게 코드를 짤 수 있다. 훨씬 편리하고, 효율적이다.
Association
Association은 has one 관계를 설정할 수 있다. 즉, Artist - Album은 1 - N 관계이다. 앨범 입장에서는 아티스트는 하나만 존재한다. 따라서, MyBatis 내 resultMap 내부에서 association을 활용해 데이터를 가져올 수 있다.
이때, join을 이용해 한번에 가져오거나 select 구문을 추가하여 가져오는 방법 중 선택할 수 있다. 아래 코드는 select 구문을 이용했다.
select를 이용한 방법
<resultMap id="albumResultMap" type="com.devop.test.core.entity.album.Album">
..(생략)
<association column="artist_seq" property="artist" select="[1] : selectArtistByPrimaryKey"/>
..(생략)
</resultMap>
<select id="[1] : selectArtistByPrimaryKey"
resultMap="artistResultMap" parameterType="java.lang.Long">
select
seq, name, debut_date
from
artists
where
seq = #{seq, jdbcType=BIGINT}
</select>
join을 이용한 방법
<resultMap id="[1] : artistResultMap" type="com.devop.test.core.entity.artist.Artist">
<id column="seq" property="seq" jdbcType="BIGINT"/>
<result column="name" property="name" jdbcType="VARCHAR"/>
<result column="debut_date" property="debutDate" jdbcType="TIMESTAMP"/>
</resultMap>
<resultMap id="albumResultMap" type="com.devop.test.core.entity.album.Album">
..(생략)
<association property="artist" resultMap="artistResultMap"/>
..(생략)
</resultMap>
<select id="[1] : selectArtistByPrimaryKey"
resultMap="artistResultMap" parameterType="java.lang.Long">
select
seq, name, debut_date
from
artists
where
seq = #{seq, jdbcType=BIGINT}
</select>
Collection
Collection은 has many 관계를 설정할 수 있다. 즉, Album - Song은 1 - N 관계이다. 아까와는 달리, Album은 하나인데 노래는 여러 곡이 존재한다.
Association과 마찬가지로, select 구문을 추가하거나 join을 이용해 가져올 수 있다.
<resultMap id="albumResultMap" type="com.devop.test.core.entity.album.Album">
..(생략)
<collection column="seq" property="songs" select="[2] : selectSongByAlbumKey"/>
..(생략)
</resultMap>
..생략
이렇게 하면 이 데이터들을 각 도메인에 적절하게 주입받아 전달할 수 있게 된다. !!
잘 활용하도록 하자.
참고를 위한 전체 코드
<?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.devop.test.core.repo.album.AlbumRepository">
<!-- Result Map 선언 -->
<resultMap id="albumResultMap" type="com.devop.test.core.entity.album.Album">
<id column="seq" property="seq" jdbcType="BIGINT"/>
<result column="title" property="title" jdbcType="VARCHAR"/>
<result column="stock" property="stock" jdbcType="INTEGER" typeHandler="org.apache.ibatis.type.EnumOrdinalTypeHandler"/>
<result column="issueDate" property="issueDate" jdbcType="TIMESTAMP"/>
<!-- association -->
<association column="artist_seq" property="artist" select="[1] : selectArtistByPrimaryKey"/>
<!-- collection -->
<collection column="seq" property="songs" select="[2] : selectSongByAlbumKey"/>
</resultMap>
<!-- SELECT Query -->
<select id="[1] : selectArtistByPrimaryKey"
resultMap="artistResultMap" parameterType="java.lang.Long">
select
seq, name, debut_date
from
artists
where
seq = #{seq, jdbcType=BIGINT}
</select>
<!-- SELECT Query -->
<select id="[2] : selectSongByAlbumKey"
resultMap="songResultMap" parameterType="java.lang.Long">
select
seq, album_seq, name, playtime
from
songs
where
album_seq = #{seq, jdbcType=BIGINT}
</select>
</mapper>