> For the complete documentation index, see [llms.txt](https://docs.sysout.co.kr/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.sysout.co.kr/web/back-end/what-is-spring-framework/spring-jdbc/mybatis/resultmap.md).

# ResultMap 활용

## ResultMap이란

myBatis에서 제공하는 자동 매핑으로 해결이 어려운 경우를 위해 구조를 설계할 수 있도록 만들어진 도구이다.

## ResultMap이 필요한 경우

`ResultMap`은 다음과 같은 데이터 구조를 불러올 때 적합하다.

### 계층형 데이터 구조

객관식 시험과 관련된 정보들을 데이터베이스에 추가한다면 다음과 같은 구조를 가지게 된다.

* 1번 시험문제
  * 1번 보기
  * 2번 보기
  * 3번 보기
  * 4번 보기
* 2번 시험문제
  * 1번 보기
  * 2번 보기
  * 3번 보기
  * 4번 보기

시험이라는 상위 entity와 보기라는 하위 entity로 나누어 생각해본다면 다음과 같이 이해할 수 있다.

<div align="left"><img src="https://4208234536-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M_TNZwLuHV9ipYLbvRq%2F-MfWmbZW3frjwUDbJfm4%2F-MfXEu_t72sdAACRfM8n%2Fimage.png?alt=media&amp;token=3324d381-f109-4b28-922d-b42d32257a79" alt=""></div>

물리 테이블은 다음과 같이 구성될 것이다.

### 문제 테이블(quiz)

| 문제번호(no) | 문제내용(text) |
| -------- | ---------- |
| 1        | 1번 문제      |
| 2        | 2번 문제      |

### 보기 테이블(choice)

| 보기번호(no) | 보기내용(text) | 문제번호(quiz) |
| -------- | ---------- | ---------- |
| 1        | 1번 보기      | 1          |
| 2        | 2번 보기      | 1          |
| 3        | 3번 보기      | 1          |
| 4        | 4번 보기      | 1          |
| 5        | 1번 보기      | 2          |
| 6        | 2번 보기      | 2          |
| 7        | 3번 보기      | 2          |
| 8        | 4번 보기      | 2          |

위의 테이블 구조를 만약 join으로 불러온다면 어떻게 될까?

```sql
select 
    문제번호, 문제내용, 보기번호, 보기내용 
from
    문제 inner join 보기 on 문제.문제번호 = 보기.문제번호;
```

다음과 같은 결과가 나올 것이다.

| 문제번호 | 문제내용  | 보기번호 | 보기내용  |
| ---- | ----- | ---- | ----- |
| 1    | 1번 문제 | 1    | 1번 보기 |
| 1    | 1번 문제 | 2    | 2번 보기 |
| 1    | 1번 문제 | 3    | 3번 보기 |
| 1    | 1번 문제 | 4    | 4번 보기 |
| 2    | 2번 문제 | 5    | 1번 보기 |
| 2    | 2번 문제 | 6    | 2번 보기 |
| 2    | 2번 문제 | 7    | 3번 보기 |
| 2    | 2번 문제 | 8    | 4번 보기 |

위의 결과를 가지고 다음 화면을 출력할 수 있는지 따져봐야 한다.

<div align="left"><img src="https://4208234536-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M_TNZwLuHV9ipYLbvRq%2F-MfWmbZW3frjwUDbJfm4%2F-MfXFVGNT_wlMiNoI8uF%2Fimage.png?alt=media&amp;token=d97eeaaa-7ba0-4d3d-9537-dbc2519251c5" alt=""></div>

join을 이용해서 위의 화면을 출력하는 것은 **가능은 하지만 어렵다**.\
조건들이 많이 등장할 것이며, 코드는 지저분하게 표시된다.

다음 화면은 만들 수 있겠지만 우리가 원하는 화면은 아니다.

<div align="left"><img src="https://4208234536-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M_TNZwLuHV9ipYLbvRq%2F-MfWmbZW3frjwUDbJfm4%2F-MfXFWnsrSdm-IFIjgN2%2Fimage.png?alt=media&amp;token=e689a367-8ee7-4605-a438-8ee1ea1ebfeb" alt=""></div>

따라서 데이터를 화면과 같은 구조로 불러오고 싶은 경우 join 대신 `ResultMap`을 이용하면 좋다.

## myBatis 설정

Spring myBatis 설정을 참고하여 마이바티스 설정을 마친다.

{% content-ref url="/pages/-MdGyBByNUSYfgCrFb5p" %}
[myBatis](/web/back-end/what-is-spring-framework/spring-jdbc/mybatis.md)
{% endcontent-ref %}

## ResultMap을 위한 클래스 구성

* 1번 시험문제
  * 1번 보기
  * 2번 보기
  * 3번 보기
  * 4번 보기
* 2번 시험문제
  * 1번 보기
  * 2번 보기
  * 3번 보기
  * 4번 보기

위와 같은 구조에서 `시험 문제`의 입장으로 바라본다면 다음과 같이 구성되어 있다고 생각할 수 있다.

* 1번 시험문제
  * List<보기>
* 2번 시험문제
  * List<보기>

따라서 시험 문제와 보기의 관점에서 각각 저장할 클래스를 만들어 `myBatis`가 구조를 이해할 수 있도록 구성한다.

### class Choice : 보기 클래스

```java
public class Choice{
    private int no;
    private String text;
}
```

보기 클래스는 `Choice`라는 이름으로 작성하였으며, 보기 번호와 내용을 가질 수 있도록 구성하였다.

### class Quiz : 문제 클래스

```java
public class Quiz{
    private int no;
    private String text;
    private List<Choice> choices;
}
```

문제 클래스는 `Quiz`라는 이름으로 작성하였으며, 퀴즈 번호와 내용, 그리고 보기 목록(`List<Choice>`)을 가질 수 있도록 구성하였다.

이 `Quiz`라는 형태를 myBatis에 ResultMap으로 등록하여 구문과 같이 연결해두면 계층형 데이터를 쉽게 불러올 수 있다.

## ResultMap 설정

ResultMap은 mapper에 설정한다.\
`quiz-mapper.xml`이라는 파일을 만들고 내부에 다음과 같이 작성한다.

```xml
<!-- 기본 설정 생략 -->
<mapper namespace="quiz">

    <resultMap type="com.hakademy.vo.Quiz" id="quiz">
		<result column="no" property="no"/>
		<result column="text" property="text"/>
		<collection column="no" 
                                    property="choices" 
                                    javaType="java.util.List" 
                                    ofType="com.hakademy.vo.Choice" 
                                    select="getChoiceList"></collection>
	</resultMap>
	
    <select id="getQuizList" resultMap="quiz">
        select * from quiz order by no asc
    </select>
    
    <select id="getChoiceList" parameterType="int" resultType="com.hakademy.vo.Choice">
        select * from choice where quiz = #{quiz}
    </select>

</mapper>
```

해당 구문을 호출할 때에는 다음과 같이 작성한다.

```java
List<Quiz> list = sqlSession.selectList("quiz.getQuizList");
```

## ResultMap 설정 상세 설명

### 메인 select 구문

```xml
<select id="getQuizList" resultMap="quiz">
    select * from quiz order by no asc
</select>
```

메인 select 구문이며, 실행 결과 형태는 미리 정의된 `quiz`라는 id를 가진 resultMap이 할당된다.\
이 구문을 실행한 결과를 myBatis가 자동으로 quiz에 맞게 처리하게 된다.

### ResultMap 설정

```xml
<resultMap type="com.hakademy.vo.Quiz" id="quiz">
    <result column="no" property="no"/>
    <result column="text" property="text"/>
    <collection column="no" 
                                property="choices" 
                                javaType="java.util.List" 
                                ofType="com.hakademy.vo.Choice" 
                                select="getChoiceList"></collection>
</resultMap>
```

ResultMap을 만들기 위해서는 다음 설정을 수행해야 한다.

* `type` : 매핑될 클래스명을 작성한다.
* `id` : 외부에서 지정할 이름을 작성한다.

내부에는 `<result>` 항목을 배치하여 데이터에 대한 실제 매핑 관계를 설정한다.

Result 설정 항목은 다음과 같다.

* `column` : 불러올 데이터베이스 항목명을 작성한다.
* `property` : 불러온 항목을 저장할 클래스 내의 변수명을 작성한다.

만약 데이터가 여러 개라면 컬렉션을 지정할 수 있는데, 이 때는 `<collection>` 항목을 배치하여 매핑 관계를 설정한다.

Collection 설정 항목은 다음과 같다.

* `column` : 불러올 데이터베이스 항목명을 작성한다. 여기서는 `하위 SQL`을 실행하기 위한 항목을 작성한다.(일반적으로 PK로 작성)
* `property` : 불러온 항목을 저장할 클래스 내의 변수명을 작성한다.
* `javaType` : Collection의 형태를 작성한다. 내장된 별칭이 있으므로 `java.util.List` 또는 `list` 라고 작성해도 무방하다.
* `ofType` : Collection의 내용물의 형태를 작성한다. `Quiz` 클래스에 `List<Choice>`라고 되어 있으므로 Choice의 경로를 작성한다.
* `select` : 이 데이터는 quiz에 존재하지 않기 때문에 조회하는 구문이 따로 필요하므로 해당하는 조회 구문의 id를 작성한다.

### 서브 select 구문

서브 select 구문은 ResultMap에 의해서 자동으로 호출된다.

```xml
<resultMap type="com.hakademy.vo.Quiz" id="quiz">
    <!-- 생략 -->
    <collection column="no" property="choices" select="getChoiceList"></collection>
</resultMap>

<select id="getChoiceList" parameterType="int" resultType="com.hakademy.vo.Choice">
    select * from choice where quiz = #{quiz}
</select>
```

필요한 부분을 제외한 나머지는 생략한 구문이다.\
ResultMap을 만들면서 `<collection>`이라는 구문을 만나면 myBatis에서는 다음의 작업을 수행한다.

1. Collection의 `column` 에서 지정한 항목을 추출한다.
2. Collection의 `select` 에서 지정한 구문을 호출하며 1번에서 추출한 값을 전달한다.
3. 서브 select 구문이 실행되어 `List<Choice>` 형태의 데이터가 반환된다.
4. Collection의 `property` 에 지정된 변수인 choices에 3번의 결과값을 설정한다.

## 결론

ResultMap을 이용하면 `계층화된 데이터`를 실제 구조와 동일하게 효과적으로 불러올 수 있다. 물론 for 구문을 이용하여 수동으로 수행해도 되지만 ResultMap을 이용하면 조금 더 구조적으로 접근할 수 있다. 단, select 구문이 여러 번(N+1) 실행되는 만큼 불러오는 데이터가 많은 경우는 권장하지 않는다.
