Spring/Spring

[Spring][Data Access] 1-4. Transaction Manager: Declarative Transaction Management - Understanding the Spring Framework’s Declarative Transaction Implementation (1)

noahkim_ 2024. 8. 11. 11:12

1. Declarative Transaction Management with AOP Proxies

@Transactional

  • 해당 에노테이션을 메소드 또는 클래스에 사용하여 적용하는 방식
항목 설명
Propagation 트랜잭션이 존재할 때 기존 트랜잭션을 사용할지, 새로 생성할지 결정
Rollback Rules 특정 예외 발생 시 트랜잭션을 롤백할지 여부 지정
Read-only 트랜잭션 내 작업이 읽기 전용임을 명시 → 성능 최적화 힌트
Isolation 동시에 실행되는 트랜잭션 간에 데이터 접근 충돌을 어떻게 처리할지 결정
Timeout 트랜잭션이 제한 시간 내에 완료되지 않으면 롤백
No rollback 특정 예외 발생 시에도 롤백하지 않도록 설정

 

 

예제

더보기
@Service
public class ProductService {

    // 기본 트랜잭션 설정: REQUIRED 전파 수준, 기본 격리 수준
    @Transactional
    public void addProduct(Product product) {
        // 트랜잭션이 시작됩니다.
        saveProduct(product);
        updateInventory(product);
        // 트랜잭션이 정상적으로 완료되면 커밋됩니다.
    }

    // 트랜잭션을 새로운 트랜잭션으로 시작 (REQUIRES_NEW)
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void saveProduct(Product product) {
        // 새로운 트랜잭션이 시작됩니다.
        // 데이터베이스에 제품 저장
    }

    // 특정 예외 발생 시 트랜잭션 롤백 (예: OutOfStockException)
    @Transactional(rollbackFor = OutOfStockException.class)
    public void updateInventory(Product product) throws OutOfStockException {
        // 재고 업데이트
        if (product.getStock() <= 0) {
            throw new OutOfStockException("Out of stock");
        }
    }

    // 읽기 전용 트랜잭션
    @Transactional(readOnly = true)
    public Product findProductById(Long id) {
        // 데이터베이스에서 제품 조회 (읽기 전용)
        return productRepository.findById(id).orElse(null);
    }
}

 

AOP Proxy

  • @Transactional이 붙은 메서드를 호출하면, 프록시 객체가 메서드 호출을 감싸 처리함
  • ✅ TransactionInterceptor가 트랜잭션 경계 설정 및 흐름 제어를 담당함 (AOP의 Advice 역할)

 

AspectJ 모드

  • 자기 호출 문제: 프록시 기반 AOP는 자기 자신의 메서드를 호출할 경우 트랜잭션이 적용되지 않음
  • ➡️ AspectJ 모드를 사용하면, 프록시가 아닌 바이트코드 조작 방식으로 동작하여 자기 호출에도 트랜잭션 적용 가능

 

예제) AspectJ 모드

더보기
@Configuration
@EnableTransactionManagement(mode = AdviceMode.ASPECTJ)
public class AppConfig {
}
@Service
public class MyService {

    @Transactional
    public void outerMethod() {
        System.out.println(">>> OUTER method called");
        innerMethod(); // 자기 호출!
    }

    @Transactional
    public void innerMethod() {
        System.out.println(">>> INNER method called");
        // 실제 DB 작업 or 테스트용 예외 발생
        throw new RuntimeException("예외 발생! 롤백되나요?");
    }
}

 

출처