이전에 JPA + Querydsl의 구성을 사용하는데 동적으로 복잡한 쿼리를 짜야할때 한계를 느껴서 Mybatis를 추가로 설정해 사용하기로 했다.
JPA의 CUD 영속성 장점과 복잡한 Read 쿼리를 짤 수 있는 Mybatis를 활용하면 좋을 것이라고 생각했기 때문이다.
Mybatis 설정만 얘기할 것이기 때문에 JPA설정은 이전글을 참고하면 된다.
https://riverblue.tistory.com/47?category=753253
Mybatis 설정
사실 설정이나 구성은 기존의 ibatis나 mybatis를 사용했다면 거의 동일한 설정이기 때문에 크게 어렵지않다.
다른점이라면 Context.xml에 TransactionManager설정할때 JPA의 트랜잭션 매니저와 Mybatis의 트랜잭션 매니저를 같이 사용해야하기 때문에 org.springframework.data.transaction.ChainedTransactionManager를 사용할 예정이다.
물론, @Transactional를 사용할때 트랜잭션 매니저 bean을 지정할 수 있다. 하지만 ChainedTransactionManager 설정 한번 하면 이전과 동일하게 사용이 가능하다.
pom.xml
<!-- MyBatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.5</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.2</version>
</dependency>
dependency를 추가해준다.
datasource-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xmlns:mybatis-spring="http://mybatis.org/schema/mybatis-spring"
xsi:schemaLocation="http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring-1.2.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.11.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- properties 파일 사용 -->
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:/datasource.properties</value>
</list>
</property>
</bean>
<!-- JPA DB Setting -->
<bean id="jobcallDataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="${JOBCALL_URL}" />
<property name="username" value="${POOZIM_USER}" />
<property name="password" value="${POOZIM_PASSWORD}" />
<property name="maxActive" value="10" />
<property name="maxIdle" value="5" />
<property name="maxWait" value="10000" />
<property name="validationQuery" value="SELECT 1" />
<property name="testOnBorrow" value="true" />
<property name="testWhileIdle" value="true" />
<property name="timeBetweenEvictionRunsMillis" value="7200000" />
<property name="removeAbandoned" value="true" />
<property name="removeAbandonedTimeout" value="60" />
<property name="logAbandoned" value="true" />
</bean>
<bean id="jpaVendorAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
</bean>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="jobcallDataSource"></property>
<property name="jpaVendorAdapter" ref="jpaVendorAdapter"></property>
</bean>
<jpa:repositories base-package="com.poozim.jobcall.repository" />
<bean id="jpaTransactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<!-- END JPA DB Setting -->
<!-- Mybatis DB Setting -->
<bean id="mybatisJobcallDataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="${JOBCALL_URL}" />
<property name="username" value="${POOZIM_USER}" />
<property name="password" value="${POOZIM_PASSWORD}" />
<property name="maxActive" value="10" />
<property name="maxIdle" value="5" />
<property name="maxWait" value="10000" />
<property name="validationQuery" value="SELECT 1" />
<property name="testOnBorrow" value="true" />
<property name="testWhileIdle" value="true" />
<property name="timeBetweenEvictionRunsMillis" value="7200000" />
<property name="removeAbandoned" value="true" />
<property name="removeAbandonedTimeout" value="60" />
<property name="logAbandoned" value="true" />
</bean>
<bean id="sqlSessionFactoryJobcall" name="sqlSessionFactoryJobcall" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="jobcallDataSource" />
<property name="typeAliasesPackage" value="com.poozim.jobcall.model" />
<property name="mapperLocations" value="classpath*:com/poozim/jobcall/*.xml" />
</bean>
<bean id="mybatisTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="mybatisJobcallDataSource" />
</bean>
<mybatis-spring:scan base-package="com.poozim.jobcall.mapper"/>
<!-- END Mybatis DB Setting -->
<!-- transaction manager setting -->
<bean id="transactionManager" class="org.springframework.data.transaction.ChainedTransactionManager">
<constructor-arg>
<list>
<ref bean="mybatisTransactionManager"/>
<ref bean="jpaTransactionManager"/>
</list>
</constructor-arg>
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
</beans>
Mybatis관련 설정을 추가해주었다.
datasource를 2개로 나눈이유는 하나의 datasource로 빈을 생성할 시, 빈은 디폴트 싱글톤이기 때문에 JPA와 Mybatis가 같은 datasource 객체를 통해 connect하게된다. 이 경우에 @transaction의 처리를 할 경우
Already value [org.springframework.jdbc.datasource.ConnectionHolder@611b35d6] for key [org.apache.commons.dbcp.BasicDataSource@1458ed9c] bound to thread [main] 같은 이미 사용된 커넥션이라는 에러를 볼 수 있다.
그러니 TransactionManager를 설정할땐 다른 Datasource 빈을 설정해주어야한다.
Mapper 인터페이스와 xml
그 외에 Mapper 인터페이스와 Mapper.xml은 원래 하던대로 하면된다.
내경우 Mapper 패키지를 새로 만들어 인터페이스들을 관리하고 mapper.xml은 Mybatis의 sqlSessionFactory설정에서 지정한 mapperLocations 디렉토리에 만들어 매핑해준다.
Mapper 인터페이스
package com.poozim.jobcall.mapper;
import java.util.List;
import com.poozim.jobcall.model.WorkGroup;
public interface WorkMapper {
public List<WorkGroup> getWorkGroupList(WorkGroup workGroup);
}
Mapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.poozim.jobcall.mapper.WorkMapper">
<select id="getWorkGroupList" parameterType="WorkGroup" resultType="WorkGroup">
SELECT *
FROM WorkGroup
</select>
</mapper>
참고하라고 이미지와 코드를 첨부한다.
테스트
테스트는 Junit 4.12를 사용했다.
package com.poozim.jobcall.service;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;
import com.poozim.jobcall.mapper.WorkMapper;
import com.poozim.jobcall.model.Work;
import com.poozim.jobcall.model.WorkGroup;
import com.poozim.jobcall.repository.WorkBoardFileRepository;
import com.poozim.jobcall.repository.WorkBoardRepository;
import com.poozim.jobcall.repository.WorkGroupMemberRepository;
import com.poozim.jobcall.repository.WorkGroupRepository;
import com.poozim.jobcall.repository.WorkRepository;
import com.querydsl.jpa.impl.JPAQueryFactory;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"file:src/main/webapp/WEB-INF/spring/*.xml",
"file:src/main/webapp/WEB-INF/spring/appServlet/*.xml"})
public class WorkServiceTest {
@Autowired
private WorkRepository workRepository;
@Autowired
private WorkGroupRepository workGroupRepository;
@Autowired
private WorkGroupMemberRepository workGroupMemberRepository;
@Autowired
private WorkBoardRepository workBoardRepository;
@Autowired
private WorkBoardFileRepository workBoardFileRepository;
@Autowired
private WorkMapper workMapper;
@Test
@Transactional
public void Test() {
//Mybatis Mapper
System.out.println(workMapper.getWorkGroupList(new WorkGroup()));
//JPA Repository
System.out.println(workGroupRepository.getWorkGroupList(10, 1));
}
}
결과 잘된다.
'Spring' 카테고리의 다른 글
Spring OCI(oracle cloude infrastructure) Java SDK 활용 (2) Object Storage에 Bucket, Object, PreAuth CRD 하기 (0) | 2021.11.05 |
---|---|
Spring OCI(oracle cloud infrastructure) Java SDK 활용 (1) Object Storage bucket 정보 가져오기 (0) | 2021.11.04 |
Spring JPA Querydsl 설정과 간단한 사용 (0) | 2021.10.29 |
Spring 메일 보내기 Mail 보내기 (0) | 2021.10.27 |
Spring JPA @Query 사용하기 JPQL (0) | 2021.10.27 |