Unexpected error occurred in scheduled task
org.springframework.transaction.UnexpectedRollbackException: Transaction silently rolled back because it has been marked as rollback-only
기존에 스프링부트 + JPA 를 사용해서
백엔드를 구축 중이었당
이번에 주기적인 알람 기능을 구현하면서
@Scheduled 어노테이션을 사용하게 되었고
자연스럽게 @Transactional 어노테이션이 붙어있던 트랜잭션에
@Scheduled 어노테이션을 붙여서 사용하게 되었다
그러나 Unexpected error occurred in scheduled task 에러
발생..
구글링을 하다가 발견한
갓영한님의 목소리
아니..!!!!!
몰랐어요..!!!!!
왜 @Scheduled 와 @Transactional 어노테이션을 분리해야 하는가
자 일단
@Transactional 은 스프링에서 제공해주는 AOP functionality 라고 한다
그래서 요 어노테이션이 붙은 메소드는
다른 bean 에서 불리웠을때 정상 동작을 한다고 한다
그리고.. 뭐.. 요렇다고 하는데..
@Transactional annotation is processed. It create proxy which store reference to original bean. Original bean is replaced to proxy in application context.
미래의 내가 이해하겠지..(?)
그래서 해결법
두 메소드를 동시에 두지 않고
한 메소드에 한 어노테이션만 두자
아래와 같은 늑낌으루~
@Service
public class UserServiceImpl implements UserService {
@Override
@Transactional
public void doSomething() {
}
}
@Service
public class UserServiceScheduler {
@Inject
private UserService service;
@Scheduled(fixedRateString = "${somestring}",initialDelayString = "${anotherstring}")
public void doSomething() {
service.doSomething();
}
}
나의 케이스
내 케이스에서는 사실
@Transactional 과 @Scheduled 를 함께 쓸 이유가 딱히 없었다
(같이 썼던 메소드가 10개라면 그중 8개 정도는 Transactional이 불필요 했던듯)
거의 그냥 db의 데이터들을 읽어오는 메소드 들이었기 때무네...
이건 그냥 내가
Transactional 어노테이션이 좋다던데~
이렇게 짧게 알아서 벌어진 일 ㅎ
그러나 아직 문제는 완벽히 해결되지 못했다
JPA repository 의 save 메소드에는
내부적으로 @Transactional 이 붙어있다고 한다
그래서 그 외부 메소드에
@Transactional 을 쓰려면
트랜잭션을 상속받는 방식중에 뭘 쓸지를 골라야 한다는것!!
결국 나는 이 문제 해결을 위해서
문제가 되는 부분들의
Transactional 어노테이션을 다 제거해버렸다
흐음 이게 맞나..?
처음에 Transactional 어노테이션을 붙였던 이유는
알람을 보내는 로직에서
1. 알람을 보냈다고 db에 알람 기록한 시점도 저장하고 (repository.save())
2. firebase 알람 시스템을 이용해서 알람을 보내는
위 2개 로직이
둘중에 실패했을때 롤백되기를 원했다
그래서 2개 로직을 하나의 함수로 묶어서
그 위에 Transactional을 달았는데..
why..
Reference
1. 나같이 @Scheduled @Transactional 같이 쓰면 웨 않되요? 하는 스택오버플로우
https://stackoverflow.com/questions/45355601/why-scheduled-annotation-doesnt-work-with-transaction-annotation-spring-boot
2. 갓영한님의 답변이 담긴 인프런 질의응답
https://www.inflearn.com/questions/297130
3. JPA의 save 에는 이미 @Transactional이 달려있는데, 외부메소드에도 @Transactional 있을때 해결책 3개 제시해주는 포스팅
https://ws-pace.tistory.com/138