
적용 위치
@Slf4j
@SpringBootTest
public class TxLevelTest {
@Autowired
LevelService levelService;
@Test
void orderTest() {
levelService.write();
levelService.read();
}
@TestConfiguration
static class TxApplyLevelConfig {
@Bean
LevelService levelService() {
return new LevelService();
}
}
@Slf4j
@Transactional(readOnly = true)
static class LevelService {
@Transactional(readOnly = false)
public void write() {
log.info("call write");
printTxInfo();
}
public void read() {
log.info("call read");
printTxInfo();
}
private void printTxInfo() {
boolean txActive = TransactionSynchronizationManager.isActualTransactionActive();
log.info("tx active={}", txActive);
boolean readOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly();
log.info("tx readOnly={}", readOnly);
}
}
}

@Transactional 의 규칙
- 우선순위 규칙
LevelService 의 타입에 @Transactional(readOnly = true) 이 붙어있다.
write() : 해당 메서드에 @Transactional(readOnly = false) 이 붙어있다.
- 이렇게 되면 타입에 있는
@Transactional(readOnly = true) 와 해당 메서드에 있는 @Transactional(readOnly = false) 둘 중 하나를 적용해야 한다.
- 클래스 보다는 메서드가 더 구체적이므로 메서드에 있는
@Transactional(readOnly = false) 옵 션을 사용한 트랜잭션이 적용된다.
- 클래스에 적용하면 메서드는 자동 적용
read() : 해당 메서드에 @Transactional 이 없다. 이 경우 더 상위인 클래스를 확인한다.
- 클래스에
@Transactional(readOnly = true) 이 적용되어 있다. 따라서 트랜잭션이 적용되고 readOnly = true 옵션을 사용하게 된다. 참고로 readOnly=false 는 기본 옵션이기 때문에 보통 생략한다.@Transactional == @Transactional(readOnly=false) 와 같다
- TransactionSynchronizationManager.isCurrentTransactionReadOnly
- 현재 트랜잭션에 적용된
readOnly 옵션의 값을 반환한다.
인터페이스에 @Transactional 적용
- 구체적인 것이 더 높은 우선순 위를 가진다
- 클래스의 메서드 (우선순위가 가장 높다.)
- 클래스의 타입
- 인터페이스의 메서드
- 인터페이스의 타입 (우선순위가 가장 낮다.)
- 클래스의 메서드를 찾고, 만약 없으면 클래스의 타입을 찾고 만약 없으면 인터페이스의 메서드를 찾고 그래도 없으면 인 터페이스의 타입을 찾는다.
- 인터페이스에
@Transactional 사용하는 것은 스프링 공식 메뉴얼에서 권장하지 않는 방법
(가급적 구체 클래스에 사용)