개발/스프링

스프링 @Transactional 사용 팁, 주의 사항

JJ Dev. 2024. 6. 9. 02:59
반응형

 

1. private 메소드에 걸린 @Transactional은 동작하지 않는다

@Service
@RequiredArgsConstructor
public class ApiService {

    private final ApiRepository apiRepository;

    @Transactional
    private void method1() {

        apiRepository.save();

    }
}
  • 이유
    • 스프링 AOP가 프록시 패턴을 사용하기 때문
    • 프록시 패턴을 쓰게 되면 외부에서 동적으로 프록시 객체를 사용하게 되는데 private 필드는 외부에서 프록시 객체를 생성할 수가 없기 때문에 AOP가 동작할수가 없다
  • 참고 글
  • Spring - private 메서드에 @Transactional이 적용될까?

 

2. 내부 호출된 @Transactional이 걸린 메소드에서는 동작하지 않는다

@Service
@RequiredArgsConstructor
public class ApiService {

    private final ApiRepository apiRepository;

    public void method1() {
        method2();
    }

    @Transactional
    public void method2() {
        apiRepository.save();
    }

}
  • 이유
    • 1번과 원리 동일
  • 해결 방법
    • 상위 메소드에 어노테이션을 붙여주어 해결할 수 있다
    • @Service @RequiredArgsConstructor public class ApiService { private final ApiRepository apiRepository; @Transactional public void method1() { method2(); } public void method2() { apiRepository.save(); } }
    • 상위 클래스에서 method1과 method2를 순차적으로 호출해서 해결할 수 있다
    • @Controller @RequiredArgsConstructor public class ApiController { private final ApiService apiService; @GetMapping("/") public void call() { apiService.method1(); apiService.method2(); } }

 

3. Checked 예외 발생 시 롤백되지 않는다

@Service
@RequiredArgsConstructor
public class ApiService {

    private final ApiRepository apiRepository;

    @Transactional
    public void method1() throws IOException {

        apiRepository.save();

        // IOException이 일어날만한 코드
        ...

    }
}
  • rollbackFor 옵션을 쓰면 해결할 수 있다
  • @Service @RequiredArgsConstructor public class ApiService { private final ApiRepository apiRepository; @Transactional(rollBackFor={IOException.class}) public void method1() throws IOException { apiRepository.save(); // IOException이 일어날만한 코드 ... } }
  • Uncheked 예외로 다시 던져서 해결할 수도 있다
  • @Service @RequiredArgsConstructor public class ApiService { private final ApiRepository apiRepository; @Transactional public void method1() throws IOException { try { apiRepository.save(); // IOException이 일어날만한 코드 ... } catch(IOException e) { throw new RuntimeException(e); } } }

 

4. Unchecked 예외를 catch하더라도 롤백은 이루어진다

@Service
@RequiredArgsConstructor
public class ApiService {

    private final ApiRepository apiRepository;

    @Transactional
    public void method1() {
        try {
            apiRepository.save();
            // IOException이 일어날만한 코드
            // ...

        } catch (RuntimeException e) {
            // ...
        }
    }
}
  • noRollbackFor 옵션으로 해결할 수 있다
@Service 
@RequiredArgsConstructor 
public class ApiService { 
    private final ApiRepository apiRepository;
    
    @Transactional(noRollbackFor={RuntimeException.class}) 
    public void method1() { 
        try { 
        	apiRepository.save(); // IOException이 일어날만한 코드 
        	// ... 
        } catch (NullPointerException e) {
        	// ... 
        } 
    } 
}

 

5. 전파 유형(Propagation)과 격리 수준(Isolation) 참고글

[Spring] @Transactional 어노테이션 이해하기(1) 전파유형(Propagation) 과 격리수준(Isolation)

반응형