@Transactional
@Transactional은 Spring에서 트랜잭션의 시작과 종료를 자동으로 관리해주는 애노테이션입니다. 이 애노테이션은 데이터의 일관성을 보장하기 위해 트랜잭션 경계를 설정하며, 작업이 실패할 경우 자동으로 롤백합니다.
- 트랜잭션 관리를 자동화 : 개발자가 직접 트랜잭션을 열고 닫는 코드를 작성할 필요 없음
- 데이터 일관성 보장 : 작업 중 하나라도 실패하면 데이터베이스에 반영되지 않도록 롤백 처리
- 동시성 문제 방지 : 여러 사용자가 동시에 데이터를 처리할 때 일관성을 유지
DB 세션
데이터베이스(DB) 세션(Session)은 애플리케이션과 데이터베이스 간의 연결 상태를 말합니다. 이 세션이 끊어지는 경우 @Transactional를 통해 해결할 수 있습니다.
1. Lazy Loading과 세션 종료
Lazy Loading은 연관 데이터를 처음부터 가져오지 않고, 실제로 필요할 때 데이터베이스에서 조회하는 방식입니다.
- 문제 상황
- 트랜잭션이 종료되면 세션도 닫힙니다.
- 세션이 닫힌 상태에서 Lazy Loading을 호출하면 데이터베이스에 접근할 수 없으므로 LazyInitializationException이 발생합니다.
@Transactional
public Order getOrder(Long orderId) {
Order order = orderRepository.findById(orderId).orElseThrow();
return order; // Lazy Loading으로 order.items를 접근하려 하면 문제가 발생
}
- order.items가 Lazy Loading으로 설정된 경우, 트랜잭션이 종료된 후 접근하면 예외가 발생합니다.
2. 예외 발생으로 인한 트랜잭션 종료
트랜잭션 내에서 예외가 발생하면 트랜잭션이 롤백되고, 데이터베이스 세션도 종료됩니다.
- 문제 상황
- 트랜잭션 종료 후 데이터를 수정하거나 조회하려 하면 세션이 없으므로 오류가 발생합니다.
@Transactional로 문제 해결
1. Lazy Loading 문제 해결
Lazy Loading 문제를 방지하려면 트랜잭션 범위 내에서 모든 데이터를 초기화해야 합니다.
- 해결 방법
- 트랜잭션을 유지한 상태에서 Lazy Loading 데이터를 로드
- 트랜잭션이 종료되기 전에 데이터를 미리 가져오기
@Transactional
public Order getOrderWithItems(Long orderId) {
Order order = orderRepository.findById(orderId).orElseThrow();
order.getItems().size(); // Lazy Loading 데이터 강제 초기화
return order;
}
2. 작업 중 예외 발생 시 rollback
트랜잭션 내에서 예외가 발생하면 @Transactional이 자동으로 트랜잭션을 롤백하여 데이터의 일관성을 보장합니다.
@Transactional
public void transferMoney(Long fromAccountId, Long toAccountId, double amount) {
Account fromAccount = accountRepository.findById(fromAccountId).orElseThrow();
Account toAccount = accountRepository.findById(toAccountId).orElseThrow();
fromAccount.debit(amount);
toAccount.credit(amount);
if (amount > 1000) {
throw new RuntimeException("Transfer limit exceeded");
}
accountRepository.save(fromAccount);
accountRepository.save(toAccount);
}
- 트랜잭션 내에서 예외가 발생하면 데이터는 자동으로 롤백됩니다.
@Transactional의 주요 옵션
1. readOnly
데이터 조회 작업에서 @Transactional(readOnly = true)를 사용하면 데이터베이스 성능을 최적화할 수 있습니다.
@Transactional(readOnly = true)
public List<Order> getAllOrders() {
return orderRepository.findAll();
}
2. rollbackFor
기본적으로 @Transactional은 RuntimeException에서만 롤백합니다. 특정 예외에서 롤백하려면 rollbackFor옵션을 사용합니다.
@Transactional(rollbackFor = CustomException.class)
public void processOrder(Order order) throws CustomException {
// CustomException 발생 시 롤백
}
3. propagation
트랜잭션의 전파 방식을 설정합니다. 기본값은 REQUIRED이며, 기존 트랜잭션이 있으면 이를 사용하고 없으면 새로 생성합니다.
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveLog(String message) {
// 새로운 트랜잭션으로 로그 저장
}
주의
- Spring 관리 빈에서만 작동
- @Transactional은 Spring이 관리하는 클래스에서만 동작합니다. 내부 메서드 호출에는 적용되지 않습니다.
- 트랜잭션 경계
- 트랜잭션 범위 내에서 모든 작업을 완료해야 합니다. 트랜잭션 종료 후 데이터를 수정하거나 조회하려 하면 오류가 발생할 수 있습니다.
- 성능 최적화
- 읽기 전용 작업에는 readOnly = true를 설정하여 성능을 향상시킬 수 있습니다.
출처
OpenAI의 ChatGPT (https://openai.com)
'Java Framework > Spring' 카테고리의 다른 글
[Spring] @Autowired (1) | 2025.02.02 |
---|---|
[Spring] @GetMapping (0) | 2025.01.31 |
[Spring] HTTP 요청 어노테이션 (Annotation) (0) | 2024.12.18 |
[Spring] URL 프리픽스(prefix) (1) | 2024.12.17 |
[Spring Boot] MVC (Model View Controller) 패턴 (0) | 2024.12.12 |