스프링(Spring)의 @Transactional
어노테이션은 스프링에서 쉽게 트랜잭션(Transaction) 설정을 할 수 있게 도와줍니다.
@Transactional
어노테이션은 선언적 트랜잭션(Declarative Transaction Management)의 한 종류입니다.
선언적 트랜잭션이란,
트랜잭션 처리를 코드 내에서 직접 제어하지 않고, 트랜잭션의 경계를 선언적으로 정의하는 방식
트랜잭션 처리를 더 간편하고 명확하게 수행
또 다른 선언적 트랜잭션 방법으로는 xml
파일을 이용할 수 있습니다.
이 글에서는 @Transactional
어노테이션을 이용하는 방법만 살펴보겠습니다.
@Service
public class MyService {
@Autowired
private JdbcTemplate jdbcTemplate;
// transaction을 설정하고 싶은 메서드에 어노테이션 추가
@Transactional
public void performTransactionalOperation() {
jdbcTemplate.update("INSERT INTO my_table (column1, column2) VALUES (?, ?)", "value1", "value2");
}
// transaction을 설정하고 싶은 메서드에 어노테이션 추가
@Transactional(readOnly = true)
public String getTransactionalData() {
return jdbcTemplate.queryForObject("SELECT column1 FROM my_table WHERE column2 = ?", new Object[]{"value2"}, String.class);
}
}
위와 같은 parameter가 있으면 하나씩 기능을 살펴보겠습니다.
트랜잭션 기능은 JDBC, JPA, Hibernate, JTA 등 어떤 API를 사용하느냐에 따라 구현 방식이 달라집니다.
Spring은 여러 트랜잭션 관리자를 지원하며 @Transactional
어노테이션의 transactionManager 파라미터로 어떤 관리자를 사용할 지 지정합니다.
그리고 Spring은 PlatformTransactionManager
라는 인터페이스로 트랜잭션 관리 기능을 추상화하여 정의합니다.
propagation을 설명하기에 앞서 물리 트랜잭션, 논리 트랜잭션이 무엇인지 알아봅시다.
데이터베이스에서 실제 실행되는 트랜잭션
데이터베이스의 Connection을 통해 실제 Commit과 Rollback 연산을 수행합니다.
애플리케이션 레벨에서 관리되는 트랜잭션
쉽게 얘기하면 하나의 @Transactional
어노테이션이 하나의 논리 트랜잭션이 됩니다.
그러면 위 그림처럼 service와 repository에 각각 @Transactional
어노테이션이 선언되어 있다고 생각해봅시다.
service가 호출되면서 트랜잭션을 수행하다가 repository 호출로 인해 새로운 논리 트랜잭션을 실행하게 됩니다.
이때, 물리 트랜잭션을 어떻게 할 것이냐 하는 문제가 발생합니다.
전파(propagation)은 메서드가 호출될 때 기존 물리 트랜잭션을 사용할지, 새로운 물리 트랜잭션을 시작할지, 또는 트랜잭션 없이 실행할지를 결정하는 스프링 트랜잭션 관리 설정입니다.
전파 관련 옵션을 다음과 같습니다.
REQUIRED
SUPPORTS
MANDATORY
REQUIRES_NEW
NOT_SUPPORTED
NEVER
NESTED
이 중 가장 대표적인, REQUIRED
와 REQUIRES_NEW
를 살펴보겠습니다.
나머지는 이 둘의 응용이므로 좀만 생각해보면 이해가 되실겁니다.
앞서 본 그림이 REQUIRED
상황입니다.
REQUIRED
는 다음과 같이 동작합니다.
repository로부터 실행된 새로운 논리 트랜잭션은 기존 service로부터 생성된 물리 트랜잭션에 참여했습니다.
REQUIRES_NEW
는 다음과 같이 동작합니다.
repository로부터 실행된 새로운 논리 트랜잭션은 service와는 별도로 물리 트랜잭션에 생성했습니다.
'격리 수준'을 설정하는 파라미터입니다.
이와 관련해서는 이 게시물에 자세히 설명해놓았습니다.
해당 트랜잭션이 읽기 전용인지 설정합니다.
select 쿼리문만 사용하는 트랜잭션에서 readOnly를 적용하면, JPA를 사용할 때 성능이 개선된다고 합니다.
트랜잭션 timeout을 설정합니다.
스프링은 기본적으로 RuntimeException과 Error 상황에 대해 Rollback을 수행합니다.
하지만 rollbackFor을 설정해 특정 checked/unchecked exception에 대해 rollback을 수행하도록 지정할 수 있습니다.
rollbackFor와 반대로 noRollbackFor을 설정해 특정 checked/unchecked exception에 대해 rollback을 수행하지 않도록 지정할 수 있습니다.