쏘댕

[MyBatis] resultMap collection 본문

공부/Java

[MyBatis] resultMap collection

ssodang 2014. 7. 10. 16:06

resultMap에 클래스를 맵핑할때 인자로 다른 클래스가 있을 경우 어떻게 받아올지 찾다가 발견!


mybatis에서 테이블간의 1:N관계를 select 할때 resultMap을 통한 일종의 서브쿼리 형식으로 데이터를 가져올수 있다. 예를들어 게시판(BOARD)라는 테이블과 댓글(COMMENT)라는 테이블이 있고, 하나의 게시글에는 여러개의 댓글이 생성될수 있음으로 게시판과 댓글의 관계는 1:N관계이다. 이런 구조를 자바 코드로 클래스를 만들어 보면 다음과 같다.

1
2
3
4
5
6
7
8
9
class  Board{
    private String board;
    private String title;
    private String content;
    private String writer;
    private List<Comment> comments;
 
    //getter, setter 생략
}

소스에서도 볼수 있듯이 Board라는 클래스는 comment의 List형태인 comments라는 프로퍼티를 가지고 있게된다. 이렇게 1:N의 구조일때 Mybatis에서는 쿼리를 저장하는 xml파일(iBatis에서는 sqlmap으로 불렀으나 mybatis에서는 mapper라고 부름)에 resultMap 엘리먼트로 다음과 같이 설정할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
<resultMap id="boradResult" type="net.krespo.mybatis.Board">
    <result property="boardid" column="BOARDID"/>
    <result property="title" column="TITLE"/>
    <result property="content" column="CONTENT"/>
    <collection property="comments" column="BOARDID" 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>

위와같이 getBoardById라는 쿼리는 게시글을 읽어올때 실행되는 쿼리이다. 이때 getBoardById는 resultMap으로 boardResult를 지정하고 있고 boardResult의 collection 선언을 통해(select="getCommentListByBoardId" 쿼리를 실행할때 parameter는 column으로 선언된 boardid를 가지고) 하위 댓글 리스트를 가져온다. 만약 댓글을 가져올때 자기가 쓴글에 자기가 쓴 댓글을 가져오려면 어떻게 해야할까?  그럴때는 boardid와 writer를 getCommentListById로 넘겨주어야 한다. 이처럼 여러개의 파라미터를 collection에서 넘길때는

column="{prop1=COLUMN1, prop2=COLUMN2}"

로 쓰면된다. . 즉

1
2
3
4
5
<collection property="comments" column="{boardid=BOARDID,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>

로 쓰면 된다. 이때 select문에서 사용하는 파라미터명과 collection에서 선언한 column의 prop1, prop2명이 반드시 동일해야 한다.(column="{prop1=COLUMN1, prop2=COLUMN2}", #{prop1}, #{prop2}) 그리고 또 반드시 주의 해야 할점은 파라미터가 한개만 전달할때는(맨 첫번째 예제인 게시글에 댓글을 가져올때) getCommentListById에 parameterType을 지정하지 않아도 됐었다. 그러나 위와 같이 collection으로 파라미터를 여러개 전달해야 할때는 반드시 parameterType="java.util.Map"을 지정해 줘야 한다. 그렇지 않으면 다음과 같은 에러를 내뿜는다.

1
Caused by: org.apache.ibatis.reflection.ReflectionException: There is no setter for property named 'boardid' in 'class java.lang.Object'


[출처] KRESPO.NET



그 다음은 내가 찾던 내용이 딱인데 iBatis여서 일단 적용해보고 다른 부분 다시 정리하는걸로!


resultMap의 property 속성 옵션 정리


ibatis에서 쿼리를 통해 실행된 결과를 리턴하는 타입을 정의하기 위한 <resultMap>을 명시한다.


[ibatis] resultClass, resultMap 사용 기본


1. <resultMap>의 <result> 옵션 값


   <resultMap id="resultMapName" class="some.domain.Class" [extends="parent-resultMap"]>
       <result property="propertyName" column="COLUMN_NAME"
                 [columnIndex="1"] [javaType="int"] [jdbcType="NUMERIC"]
                [nullValue="-999999"] [select="someOtherSatement"] />

    </resultMap>
    
빨간색으로 표시한 부분이 속성 값이며, [괄호]로 둘러싼 부분은 옵션이다.

<resultMap> 은 자신의 id 를 가지며, class 에서 반환할 클래스의 형을 명시한다. 이 클래스는 아래 <result>의 속성들으 포함하여, result 반환 객체를 매핑하고 생성한다.
extends 옵션은 다른 resultMap의 이름을 명시하며, 이 명시된 resultMap의 <result> 프로퍼티들을 모두 포함한다. 이것은 자바의 상속을 이용해 확장하는 것과 같다.

resultMap의 <result> 프로퍼티 들은 자바 객체의 변수로 선언되어 잇어야 하며, get/set 메소드가 설정되어 있어야 한다.

(1) property

- result map의 property 속성은 mapping statement에 의해 반환되는 result 객체의 자바빈즈 프로퍼티(get메소드) 이름이다.

(2) column

- column 속성 값은 결과 쿼리 결과 ResultSet 내의 칼럼의 이름이다.

(3) columnIndex

- 옵션값으로서 columnIndex 는 ResultSet 내의 컬럼의 인덱스이다.

(4) jdbcType

- jdbcType 속성은 ResultSet 컬럼의 데이터베이스 컬럼 타입을 명시적으로 정의하는데 사용한다.
- Date 타입 같은 경우에 명시적으로 선언할 때 필요하다. JAVA는 하나의 Date 타입을 가지지만, SQL 데이터베이스는 여러 타입의 Date를 가진다. 이것을 jdbcType 을 통해 명시적으로 지시할 수 있다.
- String 타입은 VARCHAR, CHAR 또는 CLOB에 의해 생성될 것이다. 이런 경우  jdbcType을 정의한다.

(5) javaType

- javaType 속성은 객체에 셋팅되는 변수 타입을 명시적으로 정의하기 위해 사용된다.
- Map과 XML mapping과 같은 매핑은 프레임워크를 위한 타입을 제공할 수 없다.
- 만약 javaType이 셋팅되지 않고 프레임워크가 그 타입을 구분할 수 없다면 타입은 객체로 가정되어 처리된다.

(6) nullValue

- nullValue 속성은 데이터베이스의 NULL 값 반환을 대신하여 사용하기 위해 값을 정의한다.
- ResultSet으로 부터 결과가 NULL이 나온 변수는 nullValue 를 통해 NULL 대신 다른 값으로 셋팅한다.

(7) select

- select 속성은 객체 사이의 관계를 서술하고 자동적으로 복합 프로퍼티 타입을 로드하는데 사용된다.
- statement의 프로퍼티 값은 다른 매핑된 statement의 이름이 되야 한다.
- column 값은 파라미터 처럼 정의되어 실행되는 statement에 전달된다.
    
    <resultMap id=”get-category-result” class=”com.ibatis.example.Category”>
          <result property=”id” column=”CAT_ID”/>
          <result property=”description” column=”CAT_DESCRIPTION”/>
          <result property=”productList” column=”CAT_ID” select=” getProductsByCatId”/>
</resultMap>

위와 같은 경우 Category 클래스의 productList 변수는 "getProductsByCatId" 라는 id 의 <statement>가 실행된 결과 값을 저장한다. column 으로 지정된 "CAT_ID" 는 <statement> "getProductsByCatId" 를 실행하기 위한 파라미터 인자로 사용된다.

"getProductsByCatId"의 파라미터가 여러개 필요할 경우는 column="{column1, column2,..}" 와 같이 나열하면 되며,
parameter에 이름 지정이 필요할 경우는 column ={"key1=column1, key2=column2..}" 의 형태로 나열하면 된다.


* <resultMap> 속성 값 사용 예


<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE sqlMap PUBLIC "-//iBATIS.com//DTD SQL Map 2.0//EN"
    "http://www.ibatis.com/dtd/sql-map-2.dtd">

    
<sqlMap namespace="Product">

   <resultMap id="getProduct" class="com.ibatis.example.Product">
       <result property="prdid" column="PRD_ID"/>
    </resultMap>
    
    <resultMap id="get-category-result" class="com.ibatis.example.Category" extends="getProduct">
       <result property="catid" column="CAT_ID" columnIndex="1" 
                    javaType="int" jdbcType="NUMERIC" nullValue="-999999" />

        <result property="prdnum" column="CAT_ID" select="getProductsByCatId"/>
    </resultMap>
    
     <statement id="selectCategory"  resultMAP="get-category-result">
       select * from CATEGORY
    </statement>
    
    <statement id="getProductsByCatId"  parameterClass="java.lang.String" resultMAP="int">
       select prdnum from PRODUCT where CAT_ID = #cat_id#
    </statement>

</sqlMap>

<resultMap> 은 get-category-result 를 id로 가지며,  com.ibatis.example.Category 를 리턴 객체로 한다.

이 <resultMap> 은 id="getProduct" <resultMap>을 상속하여, "getProduct" 의 프로퍼티들을 포함한다.


"getProduct" <resultMap> 외의 추가적으로 "catid" 와 "productList" 변수를 Category 객체에 셋팅한다.

"catid" 는 데이터베이스 컬럼 "CAT_ID" 에 매핑되며 인덱스는 "1"로 지정된다.

데이터 베이스에서 조회되는 jdbcType 은 "NUMERIC"이며, 이것을 javaType 인 "int" 형으로 변환하여 객체의 변수에 셋팅한다.

"productList"는 <statement id="getProductByCatId"> 쿼리의 결과 값을 셋팅한다. "getProductByCatId" 를 실행하기 위해서 column 값인 "CAT_ID" 가 파라미터로 넘겨준다.


[출처] 개발이 하고 싶어요


Comments