Post

MyBatis 소개 및 동적 SQL

1. MyBatis란?

마이바티스(MyBatis)는 자바 퍼시스턴스 프레임워크의 하나로 XML 서술자나 애너테이션(annotation)을 사용하여 저장 프로시저나 SQL 문으로 객체들을 연결시킨다.

마이바티스는 개발자가 지정한 SQL, 저장프로시저 그리고 몇가지 고급 매핑을 지원하는 퍼시스턴스 프레임워크이다. 마이바티스는 JDBC로 처리하는 상당부분의 코드와 파라미터 설정및 결과 매핑을 대신해준다. 마이바티스는 데이터베이스 레코드에 원시타입과 Map 인터페이스 그리고 자바 POJO 를 설정해서 매핑하기 위해 XML과 애노테이션을 사용할 수 있다.

아파치 라이선스 2.0으로 배포되는 자유 소프트웨어이며, IBATIS 3.0의 포크이며 IBATIS의 원 개발자들이 포함된 팀에 의해 유지보수되고 있다.

2. 동적 SQL

마이바티스의 가장 강력한 기능 중 하나는 동적 SQL을 처리하는 방법이다.

동적 SQL 엘리먼트들은 JSTL이나 XML기반의 텍스트 프로세서를 사용해 본 사람에게는 친숙할 것이다. 마이바티스의 이전 버전에서는 알고 이해해야 할 엘리먼트가 많았다. 마이바티스 3 에서는 이를 크게 개선했고 실제 사용해야 할 엘리먼트가 반 이하로 줄었다. 마이바티스의 다른 엘리먼트의 사용을 최대한 제거하기 위해 OGNL 기반의 표현식을 가져왔다.

1) if

동적 SQL 에서 가장 공통적으로 사용되는 것으로 where의 일부로 포함될 수 있다.

1
2
3
4
5
6
7
<select id="findActiveBlogWithTitleLike" resultType="Blog">
    SELECT * FROM BLOG
    WHERE state = 'ACTIVE'
    <if test="title != null">
        AND title like #{title}
    </if>
</select>

이 구문은 선택적으로 문자열 검색 기능을 제공할 것이다. 만약에 title 값이 없다면 모든 active 상태의 Blog 가 리턴될 것이다. 하지만 title 값이 있다면 그 값과 비슷한 데이터를 찾게 될 것이다.

2) choose, when, otherwise

적용 할 모든 조건을 원하는 대신에 한가지 경우만을 원할 수 있다. 자바에서는 switch 구문과 유사하며 마이바티스에서는 choose 엘리먼트를 제공한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<select id="findActiveBlogLike" resultType="Blog">
    SELECT * FROM BLOG WHERE state = ‘ACTIVE’
    <choose>
        <when test="title != null">
            AND title like #{title}
        </when>
        <when test="author != null and author.name != null">
            AND author_name like #{author.name}
        </when>
        <otherwise>
            AND featured = 1
        </otherwise>
    </choose>
</select>

3) trim, where, set

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<select id="findActiveBlogLike" resultType="Blog">
    SELECT * FROM BLOG
    <where>
        <if test="state != null">
            state = #{state}
        </if>
        <if test="title != null">
            AND title like #{title}
        </if>
        <if test="author != null and author.name != null">
            AND author_name like #{author.name}
        </if>
    </where>
</select>

where 엘리먼트는 태그에 의해 컨텐츠가 리턴되면 단순히 “WHERE”만을 추가한다. 게다가 컨텐츠가 “AND”나 “OR”로 시작한다면 그 “AND”나 “OR”를 지워버린다.

만약에 where 엘리먼트가 기대한 것처럼 작동하지 않는다면 trim 엘리먼트를 사용자 정의할 수도 있다. 예를들어 다음은 where 엘리먼트에 대한 trim 기능과 동일하다.

1
2
3
<trim prefix="WHERE" prefixOverrides="AND |OR ">
    ...
</trim>

override 속성은 오버라이드하는 텍스트의 목록을 제한한다. 결과는 override 속성에 명시된 것들을 지우고 with 속성에 명시된 것을 추가한다.

다음 예제는 동적인 update 구문의 유사한 경우이다. set 엘리먼트는 update 하고자 하는 칼럼을 동적으로 포함시키기 위해 사용될 수 있다.

1
2
3
4
5
6
7
8
9
10
<update id="updateAuthorIfNecessary">
    update Author
    <set>
        <if test="username != null">username=#{username},</if>
        <if test="password != null">password=#{password},</if>
        <if test="email != null">email=#{email},</if>
        <if test="bio != null">bio=#{bio}</if>
    </set>
    where id=#{id}
</update>

여기서 set 엘리먼트는 동적으로 SET 키워드를 붙히고 필요없는 콤마를 제거한다. 아마도 trim 엘리먼트로 처리한다면 아래와 같을 것이다.

1
2
3
<trim prefix="SET" suffixOverrides=",">
    ...
</trim>

4) foreach

동적 SQL 에서 공통적으로 필요한 것은 collection 에 대해 반복처리를 하는 것이다. 종종 IN 조건을 사용하게 된다.

1
2
3
4
5
6
7
8
9
<select id="selectPostIn" resultType="domain.blog.Post">
    SELECT *
    FROM POST P
    WHERE ID in
    <foreach item="item" index="index" collection="list"
             open="(" separator="," close=")">
        #{item}
    </foreach>
</select>

foreach엘리먼트는 매우 강력하고 collection 을 명시하는 것을 허용한다. 엘리먼트 내부에서 사용할 수 있는 item, index두가지 변수를 선언한다. 이 엘리먼트는 또한 열고 닫는 문자열로 명시할 수 있고 반복간에 둘 수 있는 구분자도 추가할 수 있다.

변수설명 
collection전달받은 인자로 List나 Array 형태만 가능 
item전달받은 인자값을 alias 명으로 대체s
open해당 구문이 시작될때 삽입할 문자열 
close해당 구문이 종료될때 삽입할 문자열 
separator반복 되는 사이에 출력할 문자열 
index반복되는 구문 번호로 0부터 순차적으로 증가 

참고: 컬렉션 파라미터로 Map이나 배열객체와 더불어 List, Set등과 같은 반복가능한 객체를 전달할 수 있다. 반복가능하거나 배열을 사용할때 index값은 현재 몇번째 반복인지를 나타내고 value항목은 반복과정에서 가져오는 요소를 나타낸다. Map을 사용할때 index는 key객체가 되고 항목은 value객체가 된다.

5) bind

OGNL표현을 사용해서 변수를 만든 뒤 컨텍스트에 바인딩한다.

1
2
3
4
5
<select id="selectBlogsLike" resultType="Blog">
    <bind name="pattern" value="'%' + _parameter.getTitle() + '%'" />
    SELECT * FROM BLOG
    WHERE title LIKE #{pattern}
</select>

6) Multi-db vendor support

_databaseId 변수로 설정된 databaseIdProvider가 동적인 코드에도 사용가능하다면 데이터베이스 제품별로 서로다른 구문을 사용할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
<insert id="insert">
    <selectKey keyProperty="id" resultType="int" order="BEFORE">
        <if test="_databaseId == 'oracle'">
            select seq_users.nextval from dual
        </if>
        <if test="_databaseId == 'db2'">
            select nextval for seq_users from sysibm.sysdummy1"
        </if>
    </selectKey>
    insert into users values (#{id}, #{name})
</insert>

[출처 및 참고]

This post is licensed under CC BY 4.0 by the author.