@Transcational이 rollback을 해주지 않았다.
Repository(=Dao)가 아닌 Service에 @Transactional 어노테이션을 걸었으면 Exception 발생 시 Rollback을 해줘야하는거 아닌가??
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Service
public class MyServiceImpl extends MyService {
@Override
@Transactional
public ResponseEntity updateBatchEnd(BatchEndUpDto dto) throws Exception {
batchRepository.updateBatchEnd(dto);
vodRepository.updateBatchEnd(dto);
encLogRepository.updateLogSched(EncLogUpdateDto.builder()
.status(dto.getStatus())
...
.elEndTime(dto.getElEndTime())
.elLog(dto.getErrMsg()) .build()
);
return ResponseEntity.status(HttpStatus.OK).body(true);
}
}
위 코드에서 잘못된 조건 처리로 인해 예상치 못한 오류가 발생했다.
그런데 ENC_LOG 에는 업데이트가 잘 되어있다?????? 🫤
매서드 위에 @Transactional이 있는데 도대체 왜 롤백되지 않은걸까???
@Transactional 어노테이션의 롤백
@Transactional 어노테이션은 예외처리 (try-catch)하지 않은 오류에 대해 모두 롤백처리 해주는 줄 알았는데 그게 아니었다.
Runtime Exception 또는 Error가 발생했을 때만 롤백 처리해주는게 기본이다.
Checked Exception 은 롤백처리 해주지 않는다.
🤔 Checked Exception이 뭐지? (프로그램이 제어할 수 없지만 개발자가 처리 가능한 예외라는데…)
출처 : https://interconnection.tistory.com/122
위 그림에서 빨간색으로 묶인 부분은 UncheckedException, 나머지는 다 CheckedException인거다.
(Error와 RuntimeException은 Unchecked Exception이고, Exception은 Checked Exception이다.)
즉, 저기 빨간 부분의 오류만 Rollback 해준다는거다.
1
2
3
4
5
@Transactional
public void test(TestEntity entity) throws Exception {
dao.save(entity);
throw new RuntimeException();
}
얘는 Rollback 해주는데,
1
2
3
4
5
@Transactional
public void test(TestEntity entity) throws Exception {
dao.save(entity);
throw new Exception();
}
얘는 안해준다..
JUnit 테스트에서는 테스트 코드는 모두 Rollback 하기 때문에 @Rollback(false) 를 붙이고 테스트 하니 정상적으로 되는것 같지 않았다.
그럼 Checked Exception은 어떻게 롤백해요?
트랜잭션 어노테이션에서 제공되는 속성 중 rollbackFor 라는게 있다.
롤백 하고자 하는 Exception을 지정할 수 있는 속성이다.
1
2
3
4
5
@Transactional(rollbackFor = Exception.class)
public void test(TestEntity entity) throws Exception {
dao.save(entity);
throw new Exception();
}
이렇게 rollbackFor 에 Exception.class 를 지정해주면 아까 rollback되지 않던 코드가 rollback 된다.
CheckedException 부모는 Exception 하나이기 때문에 rollbackFor에는 Exception.class 하나만 지정해주면 모든 Exception에 대해 롤백 할 수 있게 된다.
반대로 특정 오류가 나도 Rollback하고싶지 않을때는 noRollbackFor 속성에 예외 클래스를 지정해주면 된다.
