본문으로 바로가기

Spring 트랜잭션 설정

category 웹/Spring 2019. 5. 6. 16:35

스프링 트랜잭션 설정은 AOP와 비슷하게 XML을 이용하거나 어노테이션을 이용합니다.

 

TransactionManager가 필요합니다.

 

pom.xml에 spring-jdbc, spring-tx 를 추가하고 마이바티스 이용시 mybatis,mybatis-spring,hikari를 추가합니다.

 

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-jdbc</artifactId>	
			<version>${org.springframework-version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-tx</artifactId>
			<version>${org.springframework-version}</version>
		</dependency>
		
		<!-- Mybatis를 위한 HikariCP, MyBatis, mybatis-spring,Log4jdbc -->
		<dependency>
			<groupId>com.zaxxer</groupId>
			<artifactId>HikariCP</artifactId>
			<version>2.7.8</version>
		</dependency>
		<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
		<dependency>
			<groupId>org.mybatis</groupId>
			<artifactId>mybatis</artifactId>
			<version>3.4.6</version>
		</dependency>
		<dependency>
			<groupId>org.mybatis</groupId>
			<artifactId>mybatis-spring</artifactId>
			<version>1.3.2</version>
		</dependency>
		<dependency>
			<groupId>org.bgee.log4jdbc-log4j2</groupId>
			<artifactId>log4jdbc-log4j2-jdbc4</artifactId>
			<version>1.16</version>
		</dependency>

이렇게 설정을 해줍니다.

 

마지막으로 xml을 이용하신다면 root-context.xml에 트랜잭션 관리하는 빈을 등록하고

 

어노테이션 기반 트랜잭션 설정 가능하게 <tx:annotation-driven> 을 등록합니다.

<root-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:context="http://www.springframework.org/schema/context"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:mybatis-spring="http://mybatis.org/schema/mybatis-spring"
	xsi:schemaLocation="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-4.3.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
	<!-- Root Context: defines shared resources visible to all other web components -->
	<!-- Connection Pool -->
	<bean id="hikariConfig" class="com.zaxxer.hikari.HikariConfig">
		<!-- log를 기록하기 위해서!! -->
		<property name="driverClassName" value="net.sf.log4jdbc.sql.jdbcapi.DriverSpy"></property>
		<property name="jdbcUrl" value="jdbc:log4jdbc:oracle:thin:@localhost:1521:XE"></property>
		<property name="username" value="book_ex"></property>
		<property name="password" value="book_ex"></property>
	</bean>
	
	<!-- HikariCP configuration -->
	<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close">
		<constructor-arg ref="hikariConfig"></constructor-arg>
	</bean>
	
	<!-- SqlSession -->
	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	
	<tx:annotation-driven/>
	
	<!-- Root Context: defines shared resources visible to all other web components -->
	<context:annotation-config></context:annotation-config>
	<!-- service 스캔 -->
	<context:component-scan base-package="org.zerock.service"></context:component-scan>
	<!-- aop 스캔 -->
	<context:component-scan base-package="org.zerock.aop"></context:component-scan>
	<!-- 프록시 생성 -->
	<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
	
	
		
</beans>

<log4j.xml>

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration PUBLIC "-//APACHE//DTD LOG4J 1.2//EN" "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">

	<!-- Appenders -->
	<appender name="console" class="org.apache.log4j.ConsoleAppender">
		<param name="Target" value="System.out" />
		<layout class="org.apache.log4j.PatternLayout">
			<param name="ConversionPattern" value="%-5p: %c - %m%n" />
		</layout>
	</appender>
	
	<!-- Application Loggers -->
	<logger name="org.zerock.controller">
		<level value="info" />
	</logger>
	
	<!-- 3rdparty Loggers -->
	<logger name="org.springframework.core">
		<level value="info" />
	</logger>
	
	<logger name="org.springframework.beans">
		<level value="info" />
	</logger>
	
	<logger name="org.springframework.context">
		<level value="info" />
	</logger>

	<logger name="org.springframework.web">
		<level value="info" />
	</logger>

	<!-- Root Logger -->
	<root>
		<priority value="warn" />
		<appender-ref ref="console" />
	</root>
	
</log4j:configuration>

<log4jdbc.log4j2.properties>

log4jdbc.spylogdelegator.name=net.sf.log4jdbc.log.slf4j.Slf4jSpyLogDelegator

이렇게 설정하면 앞으로 어노테이션을 추가하여 트랜잭션을 관리합니다.

 

예제 실습을 위해 샘플 테이블 2개를 생성합니다.

 

create table tbl_sample1(col1 varchar2(500));
create table tbl_sample2(col2 varchar2(50));

두 테이블에 동시에 insert하는 상황을 구현합니다.

 

즉 샘플 1은 500으로 충분히 insert 되지만 샘플 2는 50으로 insert되지 않을 수도 있습니다.

 

Sample1Mapper와 Sample2Mapper 인터페이스를 추가해줍니다. (마이바티스)

 

아래와 같습니다. 원래는 xml과 인터페이스를 이용하지만 여기서는 바로 인터페이스만을 이용합니다.

 

package org.zerock.mapper;

import org.apache.ibatis.annotations.Insert;

public interface Sample1Mapper {

	@Insert("insert into tbl_sample1 (col1) values (#{data}) ")
	public int insertCol1(String data);
}
package org.zerock.mapper;

import org.apache.ibatis.annotations.Insert;

public interface Sample2Mapper {

	@Insert("insert into tbl_sample2 (col2) values (#{data}) ")
	public int insertCol1(String data);
}

 

이제 비지니스 계층과 트랜잭션을 설계합시다.

 

인터페이스를 작성하고  구현합니다.

package org.zerock.service;

public interface SampleTxService {
	public void addData(String value);
}
package org.zerock.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.zerock.mapper.Sample1Mapper;
import org.zerock.mapper.Sample2Mapper;

import lombok.Setter;
import lombok.extern.log4j.Log4j;

@Service
@Log4j
public class SampleTxServiceImpl implements SampleTxService{

	@Setter(onMethod_ = {@Autowired})
	private Sample1Mapper mapper1;
	
	@Setter(onMethod_ = {@Autowired})
	private Sample2Mapper mapper2;
	
	
	@Override
	public void addData(String value) {
		// TODO Auto-generated method stub
		log.info("mapper1............");
		mapper1.insertCol1(value);
		
		log.info("mapper2............");
		mapper2.insertCol2(value);
		
		log.info("end................");
		
	}

	
}

 

이를 테스를 해봅시다. 테스트 코드는 아래와 같습니다.

 

package org.zerock.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 lombok.Setter;
import lombok.extern.log4j.Log4j;

@RunWith(SpringJUnit4ClassRunner.class)
@Log4j
@ContextConfiguration({"file:src/main/webapp/WEB-INF/spring/root-context.xml"})
public class SampleTxServiceTests {
	
	@Setter(onMethod_= {@Autowired})
	private SampleTxService service;
	
	@Test
	public void testLong() {
		String str = "Starry\r\n"+
					"Starry night\r\n" +
					"Paint your palette blue and grey \r\n" +
					"Look out on a summer's Day";
		log.info(str.getBytes().length);
		
		service.addData(str);
	}

}

 

위 테스트 메서드는 50바이트 넘고 500바이트 넘지않는 문자열을 sample1과 sample2 테이블에 insert 합니다. 

 

그러면 sample1에는 데이터가 추가 되고 sample2는 실패합니다.

sample1 성공
sample2 실패

위와 같이 sample2에는 데이터가 들어가지 않음을 알 수 있습니다.

 

트랜잭션 처리를 하지 않았기에 sample1에만 들어간 것을 볼 수 있습니다.

 

트랜잭션 처리를 했을 경우에는 두 테이블 모두 데이터가 들어가 있으면 안됩니다.

 

이를 위해서 서비스 구현 부에 @Transactional을 추가합니다.

 

다시금 테스트를 위해 삽입 된 sample1의 테이블 내용을 지워주고 커밋합니다.

 

delete tbl_sample1;
delete tbl_sample2;
commit;

다시 테스트를 실행 하면 롤백이 되는 것을 볼 수 있습니다.

 

 

' > Spring' 카테고리의 다른 글

웹 시큐리티(Web Security)-2Day  (0) 2019.05.26
웹 시큐리티(Web Security)-1Day  (0) 2019.05.25
Spring 트랜잭션 & 데이터베이스 설계  (0) 2019.05.06
AOP 적용  (0) 2019.05.04
AOP 개념  (0) 2019.05.03