[데브코스] Spring Transaction

Updated: Categories:

W4D5 Part2 - TransactionTemplate / @Transactional 에 대해 공부하자!

Spring TransactionTemplate

  • 의존성은 spring-boot-starter-jdbc 모듈에 포함되어있음

필요 Bean

  • 아래와 같이 의존성을 주입해야한다.
    • DataSource -> PlatformTransactionManager -> TransactionTemplate
@Bean
public PlatformTransactionManager platformTransactionManager(DataSource dataSource) {
    return new DataSourceTransactionManager(dataSource);
}

@Bean
public TransactionTemplate transactionTemplate(PlatformTransactionManager platformTransactionManager) {
    return new TransactionTemplate(platformTransactionManager);
}

사용법

  • 주입받은 transactionTemplate으로 TransactionCallback을 실행시킴
  • 필요 오버라이딩 메소드 안에 쿼리실행계획을 작성
public void 메소드(...){
    transactionTemplate.execute(new TransactionCallbackWithoutResult() {
        @Override
        protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
			jdbcTemplate.쿼리실행1
			jdbcTemplate.쿼리실행2
			...
        }
    });
}


@Transactional

  • TransactionTemplate을 사용할수도 있지만, 어노테이션을 활용해 훨씬 간결하게 트랜잭션을 구현할 수 있음

설정

  • App Context가 트랜잭션 어노테이션을 사용할 수 있도록 등록해준다
@EnableTransactionManagement
... App Context ...

사용법

  • 트랜잭션이 필요한 메소드에 어노테이션을 달아준다 (끝임 넘나 간단)
  • Bean에 포함된 메소드에만 사용 가능
@Transactional
... 메소드 ...


트랜잭션 전파

  • 트랜잭션이 nested 구조로 되어있을 경우, 다양한 옵션으로 트랜잭션을 제어할 수 있음
  • default 값은 REQUIRED 이다.
@Transactional(propagation = Propagation.옵션)

내부 트랜잭션을 기준으로 설명되었습니다.

옵션 설명
REQUIRED 외부에 실행중인 트랜잭션이 있다면 그 트랜잭션에 포함된다. 없다면 새로운 트랜잭션을 실행한다.
REQUIRED_NEW 외부에 실행중인 트랜잭션이 있어도 포함되지 않고 새로운 트랜잭션을 실행한다.
SUPPORTS 외부에서 실행중인 트랜잭션이 있다면 그 트랜잭션에 포함된다. 없다면 그냥 PASS
NOT_SUPPORTED 외부에서 실행중인 트랜잭션이 있어도 NOT_SUPPORTED 어노테이션이 달린 메소드는 제외된다.
MANDATORY REQUIRED와 동일 + 호출 전 반드시 외부에 실행중인 트랜잭션이 존재해야한다.
NEVER MANDATORY와 정반대. 트랜잭션 실행중엔 해당 메소드는 실행될 수 없음
NESTED 외부 트랜잭션이 존재하면 중첩 트랜잭션에서 실행되어야 한다.


트랜잭션 격리

  • 트랜잭션 어노테이션 설정시 격리 수준을 지정할 수 있음
  • default 값은 DEFAULT이며, 이는 사용 DBMS의 격리 수준을 따른다는 것을 의미함
    • MySQL의 격리수준은 REPEATABLE_READ이다.
@Transactional(isolation = Isolation.옵션)

ACID 중 독립성과 영속성을 해치는 사례에 대해 알아보자

image

문제 상황

  • dirty reads : T1이 데이터를 변경 후 T2에서 해당 데이터를 읽었는데 T1이 커밋되지 않으면 T2의 데이터는 의미가 없음
    • 수정중인(커밋되지 않은) 데이터도 읽는 상황
  • non-repeatable reads : T1이 쿼리를 두번 수행하는데, 첫 쿼리 실행 후 T2가 데이터를 변경+커밋하면 T1 두번째 쿼리의 결과가 첫 쿼리와 다름
    • 한 트랜잭션 내 동일한 쿼리의 결과가 비일관적인 상황
  • phantom reads : T1이 데이터에 접근해 첫번째 결과를 얻었는데, T2이 데이터에 접근해 삽입해 버린다면?
    T1의 두번째 쿼리는 데이터가 추가된 결과를 얻게됨
    • 두번째 쿼리에서 유령 레코드가 나타나는 현상

격리 수준

  • READ_UNCOMMITTED : 커밋되지 않은 데이터도 읽는다.
  • READ_COMMITTED : 커밋 완료된 데이터만 읽는다.
  • REPEATABLE_READ : 데이터를 한 트랜잭션에서 읽었다면 해당 데이터는 다른 트랜잭션에서 변경할 수 없음
  • SERIALIZABLE : REPEATABLE_READ + 삽입까지 방지