스프링 트랜잭션 설정은 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는 실패합니다.
위와 같이 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 |