티스토리 뷰

spring

day2 class07 트랜잭션 처리

일상다반ㅅㅏ 2019. 1. 14. 23:31

스프링과 비교되는 EJB는 모든 비즈니스 메소드에 대한 트랜잭션 관리를 EJB 컨테이너가 자동으로 처리해준다. 스프링에서도 EJB와 마찬가지로 트랜잭션 처리를 컨테이너가 자동으로 처리하도록 설정할 수 있는데, 이를 선언적 트랜잭션 처리라고 한다.


1. 트랜잭션 네임스페이스 등록

- applicationContext.xml의 namespace탭에서 tx를 선택한다.



2. 트랜잭션 관리자 등록

- 어떤 기술을 이용하여 데이터베이스 연동을 처리했느냐에 따라 트랜잭션 관리자가 달라진다.

- 모든 트랜잭션 관리자는 PlatformTransactionManager 인터페이스를 구현한 클래스들이다.

1
2
3
4
5
6
public interface PlatformTransactionManager {
 
    TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;
    void commit(TransactionStatus status) throws TransactionException;
    void rollback(TransactionStatus status) throws TransactionException;
}
cs



3. applicationContext.xml 설정

3-1. DataSourceTransactionManager 클래스 <bean> 등록

- DataSourceTransactionManager를  <bean> 등록했다고 자동으로 트랜잭션이 관리되지 않는다.

- DataSourceTransactionManager의 commit(), rollback()를 재정의하여 구현했을 뿐이기 때문에 PlatformTransactionManager 객체 스스로 자신이 가진 메소드를 실행할 수 없다.

- 어드바이스를 이용하여 예외 처리와 트랜잭션을 처리한다.


3-2. 트랜잭션 어드바이스 설정

- 지금까진 AOP 관련 설정에 사용한 모든 어드바이스 클래스를 직접 구현했지만, 트랜잭션 관리 기능의 어드바이스는 직접 구현하지 않으며, 스프링 컨테이너가 <tx:advice>를 참조하여 자동으로 생성한다.

> 트랜잭션 관리 어드바이스 객체에 클래스 이름이나 메소드를 확인할 수 없다는 의미이기도 하다.

- get으로 시작하는 모든 메소드는 일기 전용으로 처리하여 트랜잭션 관리 대상에서 제외하고 나머지 메소드들은 트랜잭션에 포함한다.


3-3. AOP 설정을 통한 트랜잭션 적용

- <tx:advice>를 통해 직접 클래스를 구현하지 않고 스프링 컨테이너가 자동으로 생성하므로 어드바이스 메소드 이름을 알 수 없다.

> 어드바이스 메소드 이름을 모르므로 <aop:aspect> 엘리먼트를 사용하지 못하고 <aop:advisor>를 사용한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<?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:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="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.2.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.2.xsd">
 
    <context:component-scan base-package="com.springbook.biz"></context:component-scan>
    
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    
    <!-- DataSource 설정 -->
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="org.h2.Driver" />
        <property name="url" value="jdbc:h2:tcp://localhost/~/test" />
        <property name="username" value="sa" />
        <property name="password" value="" />
    </bean>
    
    <!-- Spring JDBC 설정 -->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    
    <!-- Transaction 설정 -->
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    
    <tx:advice id="txAdvice" transaction-manager="txManager">
        <tx:attributes>
            <tx:method name="get*" read-only="true"/>
            <tx:method name="*"/>
        </tx:attributes>
    </tx:advice>
    
    <aop:config>
        <aop:pointcut expression="execution(* com.springbook.biz..*(..))" id="txPointcut"/>
        <aop:advisor pointcut-ref="txPointcut" advice-ref="txAdvice"/>
    </aop:config>
</beans>
cs



4. 트랜잭션 설정 테스트

- 고의로 예외사항을 만들기 위해 insert를 같은 seq로 두번하도록 설정한다.

> 트랜잭션은 메소드 단위로 관리된다.

- seq를 100으로 설정한다.

> 실행 시 DuplicateKeyException에러가 발생한다.

- 29라인을 주석 처리 후 실행하면 클이 등록되지 않은것을 확인 할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// BoardServiceImpl.java
 
public void insertBoard(BoardVO vo) {
    boardDAO.insertBoard(vo);
    boardDAO.insertBoard(vo);
}
 
 
// BoardDAOSpring.java
 
private final String BOARD_INSERT = "insert into board(seq, title, writer, content) values(?,?,?,?)";
// ~~~
    
// CRUD 기능의 메소드 구현
// 글 등록
public void insertBoard(BoardVO vo) {
    System.out.println("===> JDBC로 insertBoard() 기능 처리");
    jdbcTemplate.update(BOARD_INSERT, vo.getSeq(), vo.getTitle(), vo.getWriter(), vo.getContent());
}
 
 
// BoardServiceClient.java
// 3. 글 등록 기능 테스트
    BoardVO vo = new BoardVO();
    vo.setSeq(100);
    vo.setTitle("임시 제목");
    vo.setWriter("홍길동");
    vo.setContent("임시 내용17");
    boardService.insertBoard(vo);
cs