[데브코스] Spring Transaction
Updated: Categories: TILW4D5 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
이다.
- MySQL의 격리수준은
@Transactional(isolation = Isolation.옵션)
ACID 중 독립성과 영속성을 해치는 사례에 대해 알아보자
문제 상황
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
+ 삽입까지 방지