ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 트랜젝션
    BackEnd/스프링 2020. 12. 6. 20:45

    트랜젝션 설정

     

    1. tx 네임스페이스를 이용한 트랜잭션 설정

    tx 네임스페이스를 통해 트랜젝션 처리된 프록시 객체를 만들어 적용한다. 

    <?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:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop"
      xsi:schemaLocation="
    http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-3.0.xsd
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop.xsd">
    
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
          <tx:method name="insert*" propagation="REQUIRED" />
        </tx:attributes>
    </tx:advice>
    <aop:config>
        <aop:pointcut expression="execution( * newlecture.dao..*.*(..) )" id="firstTransactionMethod" />
        <aop:advisor advice-ref="txAdvice" pointcut-ref="firstTransactionMethod" />
    </aop:config>
    
    </beans>

     

     

    2. @Transactional 어노테이션을 이용한 설정

    @Transactional 은 스프링 AOP를 기반하고 있는데 스프링 AOP 는 다이나믹 프록시를 기반으로 동작한다.


    트랜젝션의 전파

    트랜젝션 설정이 된 메서드 내에서 또 다른 트랜젝션이 설정된 메서드를 호출할 때 트랜젝션의 전파 방식을 선택할 수 있다.

    예를 들면 아래 코드에서 insertAndPlusCount() 메소드 내 insert() 가 성공, plusCount() 가 실패할 때 트랜젝션을 어떻게 할 것인지 정할 수 있다.

    @Transactional
    public int insert(){
            ...
    }
    @Transactional
    public int plusCount(){
        ...
    }
    
    public void insertAndPlusCount(){
        insert();
        plusCount();
    }

    insertAndPlusCount 에는 @Transactional 어노테이션이 없으니 별도의 트랜젝션 설정이 없다는 의미이다.
    그러면 insert와 plusCount는 각각 본인의 트랜젝션을 가지고 실행되며, 자신이 성공하면 다른 메서드와 관계 없이 
    commit 된다.

    하지만 만약 아래처럼 insertAndPlusCount() 에 자신의 트랜젝션이 설정되어 있다면 
    insert 와 plusCount 는 insertAndPlusCount 의 트랜젝션에서 실행되기 때문에 둘 중 하나라도 예외가 나면 
    모두 rollback 처리가 된다.

    @Transactional 
    public void insertAndPlusCount(){
        insert();
        PlusCount();
    }

     

    ▶ REQUIRED
     - 디폴트 속성, 부모 트랜잭션 내에서 실행하며 부모 트랜잭션이 없을 경우 새로운 트랜잭션을 생성한다.

    ▶ SUPPORTS
     - 이미 시작된 트랜잭션이 있으면 참여하고 그렇지 않으면 트랜잭션 없이 진행하게 만든다. 

    ▶ REQUIRES_NEW
     - 부모 트랜잭션을 무시하고 무조건 새로운 트랜잭션이 생성

    ▶ MANDATORY
     - REQUIRED와 비슷하게 이미 시작된 트랜잭션이 있으면 참여한다. 
     - 반면에 트랜잭션이 시작된 것이 없으면 새로 시작하는 대신 예외를 발생시킨다. 
     - 혼자서는 독립적으로 트랜잭션을 진행하면 안 되는 경우에 사용한다.

    ▶ REQUIRES_NEW
     - 항상 새로운 트랜잭션을 시작한다.
     - 이미 진행 중인 트랜잭션이 있으면 트랜잭션을 잠시 보류시킨다.

    ▶ NOT_SUPPORTED
     - 트랜잭션을 사용하지 않게 한다.
     - 이미 진행 중인 트랜잭션이 있으면 보류시킨다.

    ▶ NEVER
     - 트랜잭션을 사용하지 않도록 강제한다.
     -0 이미 진행 중인 트랜잭션도 존재하면 안된다 있다면 예외를 발생시킨다.

    ▶ NESTED
     - 이미 진행중인 트랜잭션이 있으면 중첩 트랜잭션을 시작한다.
     - 중첩 트랜잭션은 트랜잭션 안에 다시 트랜잭션을 만드는 것이다.

     

    주의

    이때, insertAndPlusCount 가 insert, plusCount 와 동일한 클래스 내에 위치한다면 이 설정은 원하는 대로 동작하지 않기 때문에 insertAndPlusCount 메서드는 다른 서비스 클래스에 위치해야한다.

     

    이유 ? 

    @Transactional 은 스프링 AOP를 기반하고 있는데 스프링 AOP 는 다이나믹 프록시를 기반으로 동작하기 때문이다.

    프록시기반 AOP의 단점 중에 하나인 프록시 내부에서 내부를 호출할 때는 부가적인 서비스(여기서는 그게 바로 트랜잭션)가 적용되지 않는다.

    호출하려는 타겟을 감싸고 있는 프록시를 통해야만 부가적인 기능이 적용되는데  프록시 내부에서 내부를 호출 할 때는 감싸고 있는 영역을 거치지 않기 때문이다.

    이와 같은 이유로 private 메소드에 @Transactional 을 붙이면 프록시가 생성되지 않기 때문에 무의미한 행위이다.


    또, 다이나믹 프록시가 적용되려면 인터페이스가 있어야 한다.

    만약 인터페이스가 없는 클래스에 트랜젝션 경계 설정으로 만들고 싶다면,

    스프링이 지원하는 클래스 프록시 모드를 사용하거나,

    JDK 다이나믹 프록시,

    CGLib 라이브러리가 제공해주는 클래스 레벨의 프록시를 사용할 수 있다.

    클래스 프록시를 적용할 때는 @Transactional 을 클래스에 부여해야한다.
    -이는 final 클래스에는 적용할 수 없다는 것과 불필요한 메서드에 트랜젝션이 적용될 수 있다는 단점을 가진다.
    클래스 프록시는 코드를 함부로 손대기 힘든 레거시 코드나 다른 여러 제한으로 인터페이스를 만들기 어려운 상황에 이용할 수 있다.

     

    클래스 프록시 설정

    <aop:config proxy-target-class="true">
        <aop:pointcut id="txPointcut" expression="execution(* *..MemberDaoImpl.*(..))" />
        <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
    </aop:config>
    
    
    출처: https://springsource.tistory.com/134 [Rednics Blog]
    1. dependecy 추가 
    <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>2.2</version>
        <type>jar</type>
        <scope>compile</scope>
    </dependency>
    
    2. servlet context에 proxy-target-class 속성 추가 (application-context가 아닌 servlet-context)
    <tx:annotation-driven proxy-target-class="true"/>
    
    
    출처: https://codediver.tistory.com/155 [코드 다이버]

     


    www.whiteship.me/spring-transactional-and-spring-aop/

     

    kouzie.github.io/spring/Spring-스프링-AOP/#aspect-어노테이션-기반-aop

     

    kouzie.github.io/spring/Spring-%EC%8A%A4%ED%94%84%EB%A7%81-%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98/#%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98-%EC%A0%84%ED%8C%8C

     

    springsource.tistory.com/134

     

    goddaehee.tistory.com/167

     

    반응형

    'BackEnd > 스프링' 카테고리의 다른 글

    필터와 인터셉터  (0) 2021.07.27
    WebFlux  (0) 2021.05.25
    restcontroller  (0) 2021.05.14
    Servlet & 스프링 web.xml 설정 (쉽게 따라하는 자바 웹개발 (백기선))  (0) 2021.01.14
    messageConverter  (0) 2020.11.22
Designed by Tistory.