MyBatis 세팅
Spring 에서 MyBatis setting 을 하는데 자꾸 error 가 난다. 계속 급한 마음에 설정을 제대로 잡을 수 없어서 오늘은 제대로 MyBatis 를 알아보려 한다.
여기 나온글은 대부분 아래 글에서 나온 이야기가 될 것이다.
MyBatis-Spring
MyBatis 를 Spring 에서 사용할 수 있도록 해주는 것이 MyBatis-Spring 이다. 이 MyBatis-Spring 을 이용하면,
- MyBatis mapper 들과 SqlSession 들을 다루고,
- 다른 bean 에 inject 해서 다른 bean 에서 사용할 수 있도록 도와주고,
- MyBatis 의 Exception 들에 대해서 Spring 의 DataAccessException 을 발생시켜 준다.
원래 MyBatis 는 xml 을 가지고 sql 을 작성하고 그것을 code 에서 사용할 수 있도록 되어 있다. 그런데 이 것을 spring 에서 사용할 수 있도록 (bean 을 만들고, inject 를 하는 등의)wrapper 를 만든 것이 MyBatis-Spring 이라고 할 수 있겠다.
MyBatis 가 어떤 모습을 띠는 지는 아래 페이지를 참고하자.
Installation
Spring 에서 MyBatis 를 이용할 수 있도록 해주는 것이 MyBatis-Spring 인데, 이 녀석을 사용하려면 아래와 같은 녀석들이 있어야 한다.
- Java 5 이상
- MyBatis-Spring 의 버전과 맞는 버전의 MyBatis 와 Spring
버전에 대한 정보는 아래 페이지를 참고하자.
이것을 Spring 에서 사용한다면, project 의 build path 에 아래의 library 들을 추가해야 한다.
- mybatis-spring-x.x.x.jar
- mybatis-spring 과 dependency 가 있는 library
이것은 개인적으로 번거럽다. maven 설정은 dependency 와 관련된 library 를 해결해 줘서 좋다.
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.1.1</version>
</dependency>
그런데 아래에서 mapper 를 자동으로 scan 하기 위해서 mybatis-spring 1.2.2 를 사용했는데, 이때는 maven 에서 자동으로 mybatis 를 가져오지 못하는 듯 했다. 그래서 아래처럼 mybatis 설정으로 추가해 줬다.
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.2.7</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.2.2</version>
</dependency>
Spring 에서 MyBatis 사용하기
MyBatis 를 Spring 에서 사용하기 위해서는 Spring Application Context 에 2가지를 정의해야 한다.
- SqlSessionFactory
- 1개 이상의 mapper interface
SqlSessionFactory 생성
이것을 MyBatis-Spring 에서는 SqlSessionFactoryBean 을 이용해서 SqlSessionFactory 를 만들 수 있다. 이녀석은 Spring 설정 file 인 .xml 에서 정의해 주면 된다.
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
</bean>
위에서 보다시피 dataSource 가 필요하다. 이 dataSource 는 Spring 에서 사용하던 DataSource 를 이야기 한다. 그리고 원래 Spring 에서 DataSource 를 사용하기 위해서 하는 설정은 해줘야 한다. 예를 들어 내가 사용하던 dataSource 는 아래와 같은 모습이다.
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="org.springframework.jdbc.datasource.DriverManagerDataSource" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
1개 이상의 mapper interface
아래와 같은 mapper 를 가지고 있다고 해보자.
public interface UserMapper {
@Select("SELECT * FROM users WHERE id = #{userId}")
User getUser(@Param("userId") String userId);
}
이녀석을 사용하려면 MapperFactoryBean 을 사용하면 된다.
<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface" value="my.sample.mapper.UserMapper" />
<property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>
scanning mapper
이 방법대신에 자동으로 mapper 를 scan 할 수 있다. 위의 MapperFactoryBean 설정대신에 간단히 아래 설정을 해주면 된다.
<mybatis:scan/> 와 @MapperScan 은 MyBatis-Spring 1.2.0. 부터 가능하고 @MapperScan 을 사용하려면 Spring 3.1+ 을 사용해야 한다. (참고)
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mybatis="http://mybatis.org/schema/mybatis-spring"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://mybatis.org/schema/mybatis-spring
http://mybatis.org/schema/mybatis-spring.xsd
">
<mybatis:scan base-package="my.sample.mapper" />
mapper class
위의 code 에서 보다시피 Mapper class 는 반드시 interface 여야 한다. 그리고 sql 문은 annotation 으로 사용하면 된다. 이것 대신에 MyBatis 의 xml 방법을 사용해도 된다.
위의 <bean> 설정으로 만들어진 bean 은 Spring 의 다른 bean 들 처럼 inject 할 수 있다. MapperFactoryBean 은 SqlSession 을 만드는 것과 closing 을 처리 해 준다. Spring transaction 이 진행중이면, SqlSession 은 transaction 이 끝났을 때 commit 을 해주거나 roll back 을 해준다.
주의할 점
내가 이전에 설정에서 계속 실패했던 이유는 MyBatis 와 MyBatis-Spring 을 같은 것이라고 여겼기 때문이다. 그래서 MyBatis 설정 + MyBatis-Spring 설정이 함께 들어가서 그것들이 엉킨듯 하다. 위에서 이야기한 대로 설정을 다시 잡아줬더니 잘 동작한다.
code examples
dynamic sql 에서 아래처럼 여러줄을 사용해서 사용하려면, <script> 를 사용하면 된다.
- https://code-examples.net/en/q/345186: 여러 code example 을 확인할 수 있다.
@Select({"<script>",
"SELECT *",
"FROM blog",
"WHERE id IN",
"<foreach item='item' index='index' collection='list'",
"open='(' separator=',' close=')'>",
"#{item}",
"</foreach>",
"</script>"})
List<Blog> selectBlogs(@Param("list") int[] ids);
kotlin 으로 작성한 mybatis java api 예제
- mybatis – MyBatis 3 | Java API
- MyBatis Dynamic SQL – Kotlin Support for MyBatis3
- mybatis-dynamic-sql/src/test/kotlin/examples/kotlin/mybatis3 at master · mybatis/mybatis-dynamic-sql · GitHub
package com.mycom.repository.product2
import org.apache.ibatis.annotations.Insert
import org.apache.ibatis.annotations.Param
import org.apache.ibatis.annotations.Select
import org.apache.ibatis.annotations.SelectProvider
import org.apache.ibatis.builder.annotation.ProviderMethodResolver
import org.apache.ibatis.jdbc.SQL
import org.springframework.stereotype.Repository
import com.mycom.model.Customer
@Repository
interface PurchaseDataSyncRepository {
// java api
// @ref https://mybatis.org/mybatis-3/java-api.html
//
// type-handler
// `mybatis.type-handlers-package=com.mycom.converter.mybatis,young.common.converter.mybatis`
// @see application.properties
@Select("SELECT id FROM customer AS t1 ORDER BY t1.id DESC LIMIT 1")
fun getTheLastCustomerId(): Long?
@Insert(
"INSERT INTO customer ",
"(id,name,business_no,",
"updated_at,created_at)",
"VALUES (",
"#{customer.id}, #{customer.name}, #{customer.businessNo}, ",
"#{customer.updatedAt}, #{customer.createdAt}",
")",
)
fun insertCustomer(
@Param("customer") customer: Customer,
): Int?
// @ref: https://mybatis.org/mybatis-3/java-api.html
@SelectProvider(
type = UserSqls::class, method = "getUserIdsFromEmailsQuery"
)
fun getUserIdsFromEmails(@Param("emails") emails: List<String>): List<HashMap<String, Long>>?
}
class UserSqls {
// @ref: https://mybatis.org/mybatis-3/statement-builders.html
fun getUserIdsFromEmailsQuery(emails: List<String>): String {
// ["myuser1@mycom.com","myuser2@mycom.com",...]
// --> ('myuser1@mycom.com','myuser2@mycom.com',...)
val emailsCond = emails.joinToString(
prefix = "('",
separator = "','",
postfix = "')",
)
return object : SQL() {
init {
SELECT("t1.email, t1.id")
FROM("user AS t1")
WHERE("t1.email IN $emailsCond")
}
}.toString()
}
}
상속(inheretance)이용
아래처럼 SQL 을 상속(extend) 해서 사용할 수 있다.
///
package young.pa.persistence.repository.product2.querybuilder
import org.apache.ibatis.jdbc.SQL
open class MySql : SQL() {
// your custom functions or overrides here
fun buildSelect(): SQL {
return this.SELECT("t1.email, t1.id")
}
}
///
class UserSqls {
fun getUserIdsFromEmailsQuery(emails: List<String>): String {
val emailsCond = emails.joinToString(
prefix = "('",
separator = "','",
postfix = "')",
)
return object : MySql() {
init {
buildSelect()
FROM("user AS t1")
WHERE("t1.email IN $emailsCond")
}
}.toString()
}
}
Source code
위에서 설명한 내용을 직접 적용한 소스는 아래에서 다운로드 할 수 있다.
댓글 없음:
댓글 쓰기