[Spring Boot] Mybatis Java Config로 설정하기

Mybatis Java Config

최근 Mybatis 레퍼런스를 보다가 스프링 부트에서 Java Config로 할 수 있는 방법을 보았다. 기존에 xml파일을 통해 맵핑시키는 방법은 소스코드와 쿼리를 분리시키기 위함으로 알고있는데 뭔가 요즘 추세가 xml파일로 Config를 따로 뽑아내는 대신 Java Annotation으로 Config를 대체하는 것 같은 추세인거 같다. 예제가 되는 mapper.xml은 다음과 같다.

1
2
3
4
5
6
<mapper namespace="com.navercorp.bootcamp.repository.ChannelMapper">
<insert id="insertChannel">
INSERT INTO Channel (id, number_of_users, last_message)
VALUE( #{id}, #{userCounts}, #{lastMessage})
</insert>
</mapper>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<mapper namespace="com.navercorp.bootcamp.repository.MessageMapper">
<resultMap id="messageMap" type="message">
<result property="messageId" column="id"/>
<result property="context" column="content"/>
<result property="type" column="type"/>
<result property="timeStamp" column="timestamp"/>
<result property="sender" column="sender_id"/>
<result property="channel" column="channel_id"/>
</resultMap>

<insert id="insertMessage">
INSERT INTO Message (content, type, timestamp, sender_id, channel_id)
VALUE( #{context}, #{type}, #{timeStamp}, #{sender}, #{channel})
</insert>

<select id="selectMessagesBeforeMessageId" parameterType="hashMap" resultType="message" resultMap="messageMap">
SELECT * FROM (
SELECT * FROM Message WHERE channel_id = #{channel} AND id <![CDATA[ < ]]> IFNULL(#{messageId}, (
(SELECT MAX(id) FROM Message WHERE channel_id = #{channel}) + 1
)) ORDER BY id DESC LIMIT 10) SortedMessages ORDER BY id ASC
</select>
</mapper>
1
2
3
4
5
<mapper namespace="com.navercorp.bootcamp.repository.UserMapper">
<insert id="insertUser">
INSERT INTO User (id, username, img_url, email) VALUE( #{userId}, #{userName}, #{url}, #{email})
</insert>
</mapper>

위와 같은 xml파일을 Java Config로 바꾸려면 다음과 같이 하면 된다.

먼저 Starter에 스캔할 Mapper 주소를 잡아준다.

1
2
3
4
5
6
7
@SpringBootApplication
@MapperScan(basePackages = "com.navercorp.bootcamp.mapper")
public class StaterApplication {
public static void main(String[] args) {
SpringApplication.run(StaterApplication.class, args);
}
}

Mapper는 다음과 같다.

1
2
3
4
5
6
7
@Mapper
public interface ChannelMapper {

@Insert("INSERT INTO Channel (id, number_of_users, last_message) " +
"VALUE( #{id}, #{userCounts}, #{lastMessage})")
void insertChannel(Channel channel);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Mapper
public interface MessageMapper {

@Insert("INSERT INTO Message (content, type, timestamp, sender_id, channel_id) " +
"VALUE( #{context}, #{type}, #{timeStamp}, #{sender}, #{channel})")
void insertMessage(Message message);

@Results({
@Result(property = "messageId", column = "id"),
@Result(property = "context", column = "content"),
@Result(property = "type", column = "type"),
@Result(property = "timeStamp", column = "timeStamp"),
@Result(property = "sender", column = "sender_id"),
@Result(property = "channel", column = "channel_id"),
})
@Select("SELECT * FROM (" +
"SELECT * FROM Message WHERE channel_id = #{channel} AND id < IFNULL(#{messageId}, (" +
"(SELECT MAX(id) FROM Message WHERE channel_id = #{channel}) + 1" +
")) ORDER BY id DESC LIMIT 10) SortedMessages ORDER BY id ASC")
List<Message> selectMessagesBeforeMessageId(Map map);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Mapper
public interface UserMapper {

@Results({
@Result(property = "userId", column = "id"),
@Result(property = "userName", column = "username"),
@Result(property = "url", column = "img_Url"),
@Result(property = "email", column = "email"),
})
@Select("SELECT * FROM User WHERE id = #{userId}")
User getUser(String userId);

@Insert("INSERT INTO User (id, username, img_url, email) VALUE( #{userId}, #{userName}, #{url}, #{email})")
void insertUser(User user);
}

오브젝트의 멤버 인스턴스와 컬럼명이 일치하지 않는 문제는 @ResultMap이나 @Result Annotation으로 해결할 수 있는데 @ResultMap을 사용할 경우 ResultMap에 해당하는 xml파일을 아직까지는 작성해 줘야한다. Java Config로 넘어오면서 xml에서는 쿼리를 분석해서 구분을 해줬는데 아직 인텔리제이에서는 Java Config에 이런 기능을 하는 플러그인은 없는것 같다. 또 다른 하나는 xml에서는 특수문자를 입력하기 위해 <![CDATA[ ${특수문자} ]]> 를 추가해야 했는데 Java Config에서는 특수문자를 사용할 수 있다.

그리고 @Results Annotation으로 만든 ResultMap을 재사용할 수 있는데 id를 붙혀주면 이를 다른 메소드에서 재사용 가능하다. 가령 다음과 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Mapper
public interface UserMapper {

@Results(id = "userResult", {
@Result(property = "userId", column = "id"),
@Result(property = "userName", column = "username"),
@Result(property = "url", column = "img_Url"),
@Result(property = "email", column = "email"),
})
@Select("SELECT * FROM User WHERE id = #{userId}")
User getUser(String userId);

@ResultMap("userResult")
@Select("blah blah some my query...")
List<User> getAllUsers();

@Insert("INSERT INTO User (id, username, img_url, email) VALUE( #{userId}, #{userName}, #{url}, #{email})")
void insertUser(User user);
}

참고

https://mybatis.org/mybatis-3/sqlmap-xml.html

https://github.com/mybatis/mybatis-3/issues/155

Author: Song Hayoung
Link: https://songhayoung.github.io/2021/02/19/Spring/mybatisJavaConfig/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.