Mybatis 1:N 관계 테이블 N+1 문제

June Lee·2021년 4월 14일
0

Database

목록 보기
19/19

Mybatis를 이용해 1:N 관계 테이블에서 값을 가져오는 방법에는 크게 3가지가 있다.

우선 첫 번째 방법은,
Domain 클래스가 실제 테이블처럼 관계를 맺고 있는 테이블의 id 값을 가지도록 한 후, Mybatis에서는 이 두 테이블을 Join해서 데이터를 가져오는 방법이다.

public class Post {
	private int postId;
    ...
	private int boardId; // fk
}

남은 두 방법은 위의 방법과는 달리, Board 클래스가 멤버변수로 Post 객체의 리스트를 갖는 형태로 구성된다.

public class Board {
	private int boardId;
    ...
    private List<Post> posts;
}

이렇게 구성해주면, 게시판이 게시물 리스트를 가진다는 점에서 실제 세계를 반영하는 객체지향 관점에 좀 더 알맞다.

그리고 위와 같은 클래스 구조를 가질 때, MyBatis에서는 두 가지 방법으로 Post를 포함하는 Board 데이터를 가져온다.

  1. Nested Select
<resultMap id="boradResult" type="net.krespo.mybatis.Board">
    <id property="boardId" column="board_id"/>
    <result property="title" column="title"/>
    <result property="content" column="content"/>
    <collection property="comments" column="comment_id" javaType="java.util.ArrayList" ofType="net.krespo.mybatis.Comment" select="getCommentListById"/>
</resultMap>
 
<select id="getBoardById" resultMap="boardResult">
    SELECT boardid, title, content FROM board WHERE boardid = #{boardid}
</select>
<select id="getCommentListById" resultType="net.krespo.mybatis.Comment">
    SELECT commentid, boardid, writer, content FROM comment WHERE boardid = #{boardId}
</select>

그러나 이렇게 Nested Select를 하는 경우, getBoardById를 한 번 호출하면 getCommentListById가 N번 호출되어, 결과적으로 select 쿼리가 N+1번 실행되는 결과를 낳는다.

cf) commentList를 얻는 질의해서 인자를 두 개 넘겨주고 싶은 경우

<collection property="comments" column="{boardId=board_id,writer=writer}" javaType="java.util.ArrayList" ofType="net.krespo.mybatis.Comment" select="getMyCommentListById"/>
 
<select id="getMyCommentListById" parameterType="java.util.Map" resultType="net.krespo.mybatis.Comment">
    SELECT id, writer, content FROM comment WHERE boardid = #{boardId} AND writer = #{writer}
</select>

(이 경우 parameterType을 Map으로 지정해줘야한다.)



  1. Nested Result
    위에서 언급한 N+1 문제를 해결하기 위해서는 Nested Result 방식을 사용하는 것이 좋다.
<resultMap id="boardResult" type="net.krespo.mybatis.Board">
    <id property="boardId" column="board_id"/> <!-- property는 도메인 클래스의 멤버변수명과 일치, column은 테이블의 컬럼명과 일치 -->
    <result property="title" column="title"/>
    <result property="content" column="content"/>
    <collection property="comments" column="comment_id" javaType="java.util.ArrayList" ofType="net.krespo.mybatis.Comment">
   		<id property="commentId" column="comment_id"/> <!-- Comment 클래스의 멤버 변수와 property 값이 일치 -->
      	<result property = "writer" column="writer"/>
      	<result property = "content" column="content" />
  </collection>
</resultMap>
<!-- mysql -->
<select id="findItemsBill" resultMap="menuInfo">
  SELECT m.id menuId, m.name menuName, m.price menuPrice,
  opt.id optionId, opt.name optionName, opt.price optionPrice
  FROM   MENU m LEFT OUTER JOIN OPTION opt
  ON (m.id = opt.menu_id)
  WHERE  m.id IN (<foreach collection="list" item="menu" separator=",">#{menu.menuId}</foreach>)
  AND    opt.id IN (
  <foreach collection="list" item="menu" separator=",">
    <foreach collection="menu.options" item="option" separator=",">
      #{option.optionId}
    </foreach>
  </foreach>)
</select>
<resultMap id="empResultMap" type="com.test.domain.emp">
	<id property="seq" colunm="seq" />
    <result property="empNm" colunm="empNm" />
    <result property="empId" colunm="orgId" />
    <collection property="orgList" resultMap="orgResultMap"/>
</resultMap>

<resultMap id="orgResultMap" type="com.test.domain.org">
    <id property="empId" colunm="orgId" />
    <result property="empNm" colunm="orgNm" />
</resultMap>

<select id="selectEmp" parameterType="hashmap" resultMap="empResultMap">
	SELECT * 
    FROM EMP A
    LEFT OUTER JOIN ORG B ON A.ORG_ID = B.ORG_ID
</select>



참고 자료
https://deveric.tistory.com/74
https://mybatis.org/mybatis-3/ko/sqlmap-xml.html

profile
📝 dev wiki

0개의 댓글

관련 채용 정보