참조 : https://www.baeldung.com/transaction-configuration-with-jpa-and-spring
1. 개요
- 본 문서는 Spring 트랜잭션을 사용하는 방법을 소개합니다. JPA 및 영속성 전략에 대해 더욱 더 깊이 있는 내용을 원하면, 이 문서를 참조하세요.
- 일반적으로, 트랜잭션을 구성하기 위한 두가지 방법으로 애노테이션 방식과 AOP 방식이 존재합니다. 각자의 장단점이 있기 때문에, 아래 문서를 참조하여 더 적절한 방식을 선택하면 되겠습니다.
2. 트랜잭션 구성하기
- Spring 3.1 에서는 @Configuration 클래스에서 사용할 수 있는 애노테이션 @EnableTransactionManagement을 제공하고, 이를 사용하면 트랜잭션 기능을 활성화할 수 있습니다.
- 그러나 Spring Boot를 사용하고 있고, 클래스 경로에 spring-data-* 또는 spring-tx에 해당하는 dependency가 있는 경우 트랜잭션 설정이 기본적으로 활성화됩니다.
@Configuration
@EnableTransactionManagement
public class PersistenceJPAConfig{
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
//...
}
@Bean
public PlatformTransactionManager transactionManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory().getObject());
return transactionManager;
}
}
- Spring 3.1 버전 아래와 같은 경우, XML 설정을 통해 트랜잭션 기능을 활성화할 수 있습니다.
<bean id="txManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="myEmf" />
</bean>
<tx:annotation-driven transaction-manager="txManager" />
3. @Transactional
- 트랜잭션을 활성화한 뒤, 애노테이션 @Transactional을 bean으로 등록된 클래스, 혹은 bean으로 등록된 클래스에 선언된 메서드에 작성하면 됩니다.
```java
@Service
@Transactional
public class FooService {
//...
}
```
- @Transactional은 다양한 설정이 존재합니다. 설정들의 기본값은 "트랜잭션 롤백은 runtime에서, 그리고 unchecked exception에서만 발생합니다." 입니다. checked exception에서는 롤백이 자동으로 수행되지 않기 때문에, rollbackFor 혹은 noRollbackFor와 같은 애노테이션 파라미터로 경우에 따라 설정해주어야 합니다.
- Propagation Type
- Isolation Level
- Timeout
- Readonly
- Rollback Rule
4. 트랜잭션 사용 시 고려해야 하는 사항
4.1 트랜잭션과 프록시
- Spring은 @Transactional이 작성된 모든 클래스에 대한 프록시 빈을 만듭니다. 이 프록시 빈은 주로 트랜잭션을 수행/커밋/롤백 등 메서드의 로직 전후에 트랜잭션 로직을 주입하는 데 사용됩니다.
- @Transactional이 작성된 빈이 interface로 구현되어 있다면, Spring은 Java Dynamic Proxy를 사용하여 프록시 빈을 생성합니다. 이는 오로지 외부에서의 메서드 호출만 프록시 로직을 수행한다는 것입니다. 같은 클래스 내부에서 호출하면, @Transactional이 작성된 메서드라고 하더라도 프록시 로직이 수행되지 않습니다.
4.2 Isolation Level
@Transactional(isolation = Isolation.SERIALIZABLE)
4.3 Readonly
- Readonly Flag는 특히 JPA가 적용된 프로젝트에서 많은 혼돈을 줍니다. 이 사항에 대해 Java에서는 아래와 같이 소개합니다.
- Readonly Flag는 실제 트랜잭션 하위 시스템에 대한 힌트 역할을 할 뿐이고, 반드시 쓰기 액세스 시도가 실패하는 것은 아닙니다. Readonly 힌트를 해석할 수 없는 Transaction Manager는 Readonly 트랜잭션을 요청해도 예외를 발생시키지 않습니다.
- Readonly Flag가 설정되어 있어도 insert / update가 발생하지 않을 것을 보장할 수 없습니다. JPA는 DB 상태를 고려하지 않고 rollback/commit 처리를 하나, DB 내부적으로 rollback/commit 처리를 할 수 있기 때문입니다. (참조 : https://stackoverflow.com/questions/10394857/how-to-use-transactional-with-spring-data)
- Readonly Flag는 트랜잭션 내부에서만 관련이 있습니다. 트랜잭션 외부에서 작업이 발생하면, Readonly는 정상적으로 수행되지 않습니다.
/*
* Propagation.SUPPORTS
* 부모 트랜잭션이 존재할 경우 부모 트랜잭션에 참여한다.
* 부모 트랜잭션이 없을 경우 non-transactional 하게 동작한다.
*/
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
- 위 코드에서, 트랜잭션이 아닌 부분에서는 Readonly는 무시됩니다.
4.3 Rollback
- Declarative 방식과 Programmatic 방식이 있으나, Declarative 방식을 추천합니다.
4.3.1 Declarative
- 원하는 Scope에서 애노테이션을 작성하여 롤백을 구현하는 방식
- @Transactional을 메서드에 다는 방식으로 트랜잭션을 구현한다. @Transactional의 속성 rollbackFor 및 rollbackForClassName을 사용하여 트랜잭션 롤백 기준을 지정할 수 있다. 또한, 속성 noRollbackFor 및 noRollbackForClassName을 사용하여 트랜잭션 롤백 회피 기준을 지정할 수 있다.
- 롤백의 기본값 : runtime 예외 발생 시 롤백 발생
// DataIntegrityViolationException은 runtime 예외이므로, courseDao.create(course) 트랜잭션의 롤백이 수행된다.
@Transactional
public void createCourseDeclarativeWithRuntimeException(Course course) {
courseDao.create(course);
throw new DataIntegrityViolationException("Throwing exception for demoing Rollback!!!");
}
// SQLException 발생 시만 롤백 처리를 한다.
// courseDao.create(course) 트랜잭션의 롤백이 수행된다.
@Transactional(rollbackFor = { SQLException.class })
public void createCourseDeclarativeWithCheckedException(Course course) throws SQLException {
courseDao.create(course);
throw new SQLException("Throwing exception for demoing rollback");
}
// SQLException 발생 시 롤백 처리를 하지 않는다.
// courseDao.create(course) 트랜잭션의 롤백이 수행되지 않는다.
@Transactional(noRollbackFor = { SQLException.class })
public void createCourseDeclarativeWithNoRollBack(Course course) throws SQLException {
courseDao.create(course);
throw new SQLException("Throwing exception for demoing rollback");
}
4.3.2 Programmatic
- 직접 코드에서 롤백을 구현하는 방식
public void createCourseDefaultRatingProgramatic(Course course) {
try {
courseDao.create(course);
} catch (Exception e) {
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
}
'Spring' 카테고리의 다른 글
[Spring] 서비스는 트랜잭션, 도메인의 순서 보장 역할만 제공한다 (0) | 2022.11.09 |
---|---|
[Spring] Async (0) | 2022.10.28 |
[Spring] 왜 Spring 인가 (0) | 2022.08.14 |
[Spring Batch] Tasklet & Chunk 예시 (0) | 2022.07.13 |