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 역할 수행
- 트랜잭션 경계 설정 및 흐름 제어
예제) 설정
더보기
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private CustomInterceptor customInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 인터셉터 등록
registry.addInterceptor(customInterceptor)
.addPathPatterns("/api/**") // 특정 경로에만 인터셉터 적용
.excludePathPatterns("/api/public/**"); // 특정 경로는 인터셉터에서 제외
}
}
예제) 등록
더보기
@Component
public class CustomInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("Pre Handle method - Intercepting the Request");
// true를 반환하면 컨트롤러로 요청이 전달됩니다.
// false를 반환하면 요청이 중단됩니다.
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
// 컨트롤러에서 요청 처리 후, 뷰 렌더링 전 실행될 로직
System.out.println("Post Handle method - Request processing is done");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
// 뷰 렌더링 후에 실행될 로직
System.out.println("After Completion method - Request is complete");
}
}
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("예외 발생! 롤백되나요?");
}
}
출처