본문 바로가기

Spring

Spring MVC JPA 에서 Mybatis 추가하기

728x90

이전에 JPA + Querydsl의 구성을 사용하는데 동적으로 복잡한 쿼리를 짜야할때 한계를 느껴서 Mybatis를 추가로 설정해 사용하기로 했다.

JPA의 CUD 영속성 장점과 복잡한 Read 쿼리를 짤 수 있는 Mybatis를 활용하면 좋을 것이라고 생각했기 때문이다.

 

Mybatis 설정만 얘기할 것이기 때문에 JPA설정은 이전글을 참고하면 된다.

https://riverblue.tistory.com/47?category=753253 

 

Spring (부트 아니고 레거시)에서 JPA 적용하기 (spirng-data-jpa)

이전 글중 eclipse 스프링 레거시 프로젝트 생성과 이어지는 내용이다. https://riverblue.tistory.com/35 " data-og-host="riverblue.tistory.com" data-og-source-url="https://riverblue.tistory.com/35" data-..

riverblue.tistory.com

 

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));
	}
}

결과 잘된다.

728x90