Transaction

흔히 java에서 트랜잭션 사용시 try catch 문 사이에 commit과 Rollback을 이용하여 처리한다.

Connection conn = null;
try {
    conn = DriverManager.getConnection(jdbcUrl, user, pw);
    conn.setAutoCommit(false);
    /*
        ...쿼리 실행..
     */
    conn.commit();
} catch (SQLException e) {
    if(conn!=null) {
        try {
            conn.rollback();
        } catch (SQLException e1) {
 
        }
    }
} finally {
    if(conn!=null) {
        try {
            conn.close();
        } catch (SQLException e) {
 
        }
    }
}

reSpring에서는 이러한 반복적인 작업을 두 가지 방법으로 한번에 해결할 수 있다.

  1. 선언에 의한 트랜잭션

  2. 프로그램에 의한 트랜잭션

선언에 의한 트랜잭션

선언에 의한 트랜잭션에는 두가지(1. AOP 2. annotation) 방법이 있다.

@Transactional Annotation

우선 annotation 사용을 위한 bean 설정을 해준다.

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/tx
            http://www.springframework.org/schema/tx/spring-tx.xsd">
     
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
          destroy-method="close">
        <property name="driverClass" value="com.mysql.jdbc.Driver"/>
        <property name="jdbcUrl" value=""/>
        <property name="user" value="user"/>
        <property name="password" value="pwd"/>
    </bean>
 
    <bean id="transactionManager"
          class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
        <tx:annotation-driven transaction-manager="transactionManager"/>
    </bean>
</beans>

다음과 같이 설정 후 원하는 메소드 위에 @Transactional 을 붙여 간편하게 트랜잭션을 구현할 수 있다.

메소드 전체를 하나의 트랜잭션으로 묶을 수 있다는 것은 @Transactional의 내부 동작이 Proxy로 이루어진다는 것을 의미한다.

@Transactional
public void something (int a) {
    ...
}

@Transactional 이 적용된 경우 트랜잭션 기능이 적용된 프록시 객체가 생성된다.

이 프록시 객체는 @Transactional이 포함된 메소드가 호출되면, PlatformTransactionManager를 사용하여 트랜잭션을 시작하고, 결과의 정상 여부에 따라 Commit 또는 Rollback 한다.

@Transactional 어노테이션에 여러가지 속성을 지정할 수 있다.

@Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public int method(int i) throws Exception {
	return sqlMapClient.delete("");
}

isolation

격리수준(트랜잭션에서 일관성이 없는 데이터를 허용하도록 하는 수준)을 말하는데 옵션은 다음과 같다.

Dirty read

위와 같이 다른 트랜잭션에서 처리하는 작업이 완료되지 않았는데도 다른 트랜잭션에서 볼 수 있는 현상을 dirty read 라고 하며, READ UNCOMMITTED 격리수준에서만 일어나는 현상

MVVC(Multi Version Concurrency Control)

MVCC는 다중 사용자 데이터베이스 성능을 위한 기술로 데이터 조회 시 LOCK을 사용하지 않고 데이터의 버전을 관리해 데이터의 일관성 및 동시성을 높이는 기술

propagation

트랜잭션 동작 도중에 다른 트랜잭션을 실행해야하는 상황이 자주 발생하게 되는데, 호출되는 트랜잭션의 입장에서는 호출한 트랜잭션을 그대로 사용할 수도 있고, 새로운 트랜잭션을 생성할 수도 있다.

  • 호출한 트랜잭션을 그대로 사용한 경우 중간에 오류가 발생하면 모든 트랜잭션이 롤백이 된다.

  • 새로운 트랜잭션을 생성한 경우 중간에 오류가 발생하면 오류가 발생한 트랜잭션이 롤백 될 것이다.

이러한 트랜잭션 관련 설정은 @Transactional의 propagation 속성을 통해 지정할 수 있다.

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void something (int a) {

}

rollback-for

특정 예외가 발생했을 경우에 롤백되도록 설정한다. 설정하지 않을 경우 오로지 RuntimeException을 상속받은 예외에만 롤백처리를 해준다.

no-rollback-for

특정 예외가 발생하더라도 롤백되지 않도록 설정한다.

참조

Last updated