배경 지식
Association & Collection
-
has one
관계를 설정하기 위한 association -
has many
관계를 설정하기 위한 collection
Mybatis에서 관계를 정의하는 방법
-
Nested Select = 1번의 추가 Select을 통한 데이터 검색
-
Nested Results = Join을 통한 한 번에 데이터를 검색
- 반드시 MyBatis의 Association과 Collection 알아보기 - Artist, Album글을 정독 후 해당 글을 읽길 바란다.
@Data
public class Artist {
private Long seq;
private String name;
private Date debutDate;
}
@Data
public class Album {
private Long seq;
private Artist artist;
private String title;
private Stock stock;
private Date issueDate;
private List<Song> songs;
public int getTotalPlaytime() {
return (getSongs() == null || getSongs().size() == 0) ? 0 :
getSongs().stream().mapToInt(Song::getPlaytime).sum();
}
}
@Data
public class Song {
private Long seq;
private Album album;
private String name;
private int playtime;
}
3. Song
-
마지막으로 Song Entity를 위한 작업을 진행해보자.
-
SongRepository 매핑 XML은 다음과 같다.
-
주의해서 볼 부분은 songResultMap에서
Song 객체와has one
관계에 있는
Album 객체를 가져오기 위한 assocication 태그 부분이다. -
2가지 방법으로 association 태그를 사용해본다.
-
결론부터 말하자면
Join을 통해 한번에 가져온 데이터를
association 태그 내에 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.song.SongRepository">
<!-- [1] : Nested Results = Join을 통한 한 번에 데이터를 검색 -->
<resultMap id="songResultMap" type="com.devop.test.core.entity.song.Song">
<id column="seq" property="seq" jdbcType="BIGINT"/>
<result column="name" property="name" jdbcType="VARCHAR"/>
<result column="playtime" property="playtime" jdbcType="INTEGER"/>
<!-- [association] 1번째 방법 -->
<association property="album" resultMap="albumResultMap"/>
<!-- [association] 2번째 방법 -->
<association property="album" column="album_seq" select="selectAlbumByPrimaryKey"/>
</resultMap>
</mapper>
<!-- [association] 1번째 방법 -->
<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 column="artist_seq" property="artist" select="selectArtistByPrimaryKey"/>
<collection column="seq" property="songs" select="selectSongByAlbumKey"/>
</resultMap>
-
여기서
<association property="album" resultMap="albumResultMap"/>
코드는 다음과 같이 이해할 수 있다. -
Album 타입의 album이라는 변수명에
@Data
public class Song {
private Long seq;
private Album album; // <-- Album 타입의 album 이라는 변수명
private String name;
private int playtime;
}
-
resultMap으로 선언한 albumResultMap형태로 값을 바인딩시킨다.
-
즉 album 변수에 albumResultMap 형태로 값이 들어가지게 된다.
- association을 사용하는 2번째 방법에 대해 알아보자.
<!-- Nested Select :: selectAlbumByPrimaryKey -->
<select id="selectAlbumByPrimaryKey"
resultMap="albumResultMap2" parameterType="java.lang.Long">
select
seq, artist_seq, title, stock, issue_date
from
albums
where
seq = #{seq, jdbcType=BIGINT}
</select>
@Service
public class ArtistService {
private Logger logger = LoggerFactory.getLogger(getClass());
@Autowired
private ArtistRepository artistRepository;
@Transactional(readOnly=true)
public Artist selectArtistByPrimaryKey(Long seq) {
try {
return artistRepository.selectArtistByPrimaryKey(seq);
} catch (DataAccessException e) {
logger.error(e.getMessage(), e);
throw e;
}
}
}
@Service
public class AlbumService {
private Logger logger = LoggerFactory.getLogger(getClass());
@Autowired
private AlbumRepository albumRepository;
@Transactional(readOnly=true)
public Album selectAlbumByPrimaryKey(Long seq) {
try {
return albumRepository.selectAlbumByPrimaryKey(seq);
} catch (DataAccessException e) {
logger.error(e.getMessage(), e);
throw e;
}
}
}
@Service
public class SongService {
private Logger logger = LoggerFactory.getLogger(getClass());
@Autowired
private SongRepository songRepository;
@Transactional(readOnly=true)
public Song selectSongByPrimaryKey(Long seq) {
try {
return songRepository.selectSongByPrimaryKey(seq);
} catch (DataAccessException e) {
logger.error(e.getMessage(), e);
throw e;
}
}
@Transactional(readOnly=true)
public List<Song> selectSongByAlbumKey(Long albumSeq) {
try {
return songRepository.selectSongByAlbumKey(albumSeq);
} catch (DataAccessException e) {
logger.error(e.getMessage(), e);
throw e;
}
}
}
Controller 추가
- 지금까지 열심히 작성한 Service를 사용하는 Spring MVC Controller를 추가해보자.
@Controller
@RequestMapping(value="album")
public class AlbumController {
@Autowired private AlbumService albumService;
@RequestMapping(value="index")
public ModelAndView selectAlbum(@RequestParam(required=true) Long seq) {
Album album = albumService.selectAlbumByPrimaryKey(seq);
ModelAndView mav = new ModelAndView();
mav.addObject("album", album);
return mav;
}
}
결과 화면
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>:: 앨범 ::</title>
</head>
<body>
<h1>앨범정보</h1>
<h2>앨범명</h2>
${album.title}<br/>
<h2>재생시간</h2>
${album.totalPlaytime}<br/>
<h2>음악가</h2>
${album.artist.name}<br/>
</body>
</html>