ResultMap

카테고리 없음 2014. 5. 28. 14:24

ResultMap에 대한 자세한 정보는 아래 링크에서 확인할 수 있다.

http://mybatis.github.io/




보통의 경우, SELECT 결과값은 자바빈이나 POJO, HashMap, 단일 값이라면 String 등으로 resultType을 결정한다.

만약 컬럼명과 프로퍼티명이 다를 경우, 데이터베이스 별칭을 사용하는 것과 다른 방법으로 명시적인 resultMap 을 선언하는 방법이 있다.


다음을 보면 :

1
2
3
4
5
<select id="selectUsers" parameterType="int" resultMap="userResultMap">
  select user_id, user_name, hashed_password
  from some_table
  where id = #{id}
</select>

여기서 selectUsers는 다음의 userResultMap을 참조하고 있다.


1
2
3
4
5
<resultMap id="userResultMap" type="com.someapp.model.User">
  <id property="id" column="user_id" />
  <result property="username" column="username"/>
  <result property="password" column="password"/>
</resultMap>

column 속성은 데이터베이스 컬럼, property는 자바빈이나 Map타입의 키값을 표현한다.


사실 resultMap을 단순히 컬럼명과 프로퍼티명이 일치하지 않을때 보다는1 좀 더 복잡한 결과를 받아와야 할 때 사용한다.



problem


그 복잡한 결과란 무엇이냐?

예를 들어 다음 그림처럼 BBS 테이블과 그것을 참조하는 Attach 테이블이 있다고 가정하자.

        (매우 간결하면서 파일첨부가 가능한 게시판의 테이블 관계도)


그리고 인터페이스 상 특정 글을 보려고 할 때의 쿼리는 다음과 같다고 하자.

1
2
3
4
5
6
7
8
<select id="selectBbsDetail" parameterType="java.lang.Integer" resultType="com.model.Bbs">
    SELECT      b.bbs_id, b.name, b.title, b.content, b.regdate,
                a.attach_id, a.file_name, a.file_size
    FROM        bbs b
    LEFT JOIN   attach a
    ON          b.bbs_id = a.bbs_id
    WHERE       b.bbs_id = #{id}
</select>


각 게시글에 단 하나의 첨부파일만 존재한다면 문제가 발생하지 않는다. 하지만, 두 테이블의 관계는 1:N 의 관계이므로 하나의 글에 여러 첨부파일이 존재할 수 있다. 그리고 그 경우라면 분명 예외가 발생할 것이다.

bbs_id  |  name  |  title  |       content          |  regdate  |  attach_id  |     file_name     |   file_size

---------------------------------------------------------------------------------------------------------------------------

  1003        me       제목         내용내용..               오늘               24            1e2435.jpg       2349890

  1003        me       제목         내용내용..               오늘               25            4e981A.jpg       5799101

  1003        me       제목         내용내용..               오늘               25            weeeee.jpg     100382901


//  org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.exceptions.TooManyResultsException



solution


위 문제는 data concatenating2(참고) 등의 방법으로 해결할 수 있으나 여기선 resultMap을 사용해보겠다.


우선 다음 코드로 출발한다 :

1
2
3
4
5
6
7
8
<select id="selectBbsDetail" parameterType="java.lang.Integer" resultMap="bbsResultMap">
    SELECT      b.bbs_id, b.name, b.title, b.content, b.regdate,
                a.attach_id, a.file_name, a.file_size
    FROM        bbs b
    LEFT JOIN   attach a
    ON          b.bbs_id = a.bbs_id
    WHERE       b.bbs_id = #{id}
</select>


참조하고 있는 bbsResultMap은 :

1
2
3
4
5
6
7
<resultMap id="bbsResultMap" type="java.util.HashMap">
  <id property="bbs_id" column="bbs_id" />
  <result property="name" column="name"/>
  <result property="title" column="title"/>
  <result property="content" column="content"/>
  <collection property="attachList" javaType="java.util.ArrayList" resultMap="bbsAttachResultMap"/>
</resultMap>


type을 자바빈이 아닌 HashMap으로 받는것 외엔 차이가 없다. 그러나 위처럼 resultMap으로 매핑할 경우 단순히 컬럼명과 프로퍼티명의 불일치만 해결해주는 것이 아니라 중복되는 결과를 받았을 때 이를 자동으로 걸러내주는 효과도 있다. 이는 하위 요소인 <id>를 사용했기 때문이다.


그리고 <collection>은 child rows3를 처리하는 방법을 나타낸다. 여기서는 bbsAttachResultMap을 참조하고 있고 이는 다음과 같다:

1
2
3
4
5
<resultMap id="bbsAttachResultMap" type="java.util.HashMap">
    <id property="attach_id" column="attach_id"/>
    <result property="file_name" column="file_name"/>
    <result property="file_size" column="file_size"/>
</resultMap>


이제 마이바티스는 다음과 같은 MAP타입의 값을 리턴할 것이다:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
{
    content=내용내용..,
    title=제목,
    bbs_id=1003,
    regdate=오늘,
    name=me,
    attachList=[
        {
            file_size=2349890,
            attach_id=24,
            file_name=1e2435.jpg
        },
        {
            file_size=5799101,
            attach_id=24,
            file_name=4e981A.jpg
        },
        {
            file_size=100382901,
            attach_id=24,
            file_name=weeeee.jpg
        }
    ]
}

이 값을 만약 자바로 하드코딩 한다면 다음과 같다:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
HashMap<String, Object> rows = new HashMap<String, Object>();
rows.put("bbs_id", "1003");
rows.put("name", "me");
rows.put("title", "제목");
rows.put("content", "내용내용..");
rows.put("regdate", "오늘");
             
ArrayList<HashMap<String, String>> list = new ArrayList<HashMap<String, String>>();
 
HashMap<String, String> childRows = new HashMap<String, String>();
childRows.put("attach_id", "24");
childRows.put("file_name", "1e2435.jpg");
childRows.put("file_size", "2349890");
list.add(childRows);
 
childRows.clear();
childRows.put("attach_id", "24");
childRows.put("file_name", "4e981A.jpg");
childRows.put("file_size", "5799101");
list.add(childRows);
 
childRows.clear();
childRows.put("attach_id", "24");
childRows.put("file_name", "weeeee.jpg");
childRows.put("file_size", "100382901");
list.add(childRows);
 
rows.put("attachList", list);






  1. 자바빈 프로퍼티나 DB 별칭을 변경하는게 불가능하지 않다면 당연히 그쪽이 더 편하니까 [본문으로]
  2. child rows를 하나의 컬럼으로 출력. MS-SQL이라면 Cross Apply / FOR XML PATH 를 사용한 방식 [본문으로]
  3. 1:N의 관계로 설정된 테이블에서 N의 레코드 [본문으로]


Posted by foryamu
,