-
@Transactional 에 대한 고찰Spring 2023. 3. 26. 21:43
스프링에서 트랜잭션 처리를 지원하는데 어노테이션 방식으로 @Transactional 을 선언하여 사용하는데,
여기서 얘기하는 트랜잭션 개념은 다른 글에서도 워낙 많은 편이니 생략하도록 하겠다.
@Transactional 이란?
메서드나 클래스에 @Transactional 추가하게 되면
자동적으로 트랜잭션을 시작하고, 정상 여부에 따라 Commit 또는 Rollback 하게 된다.
위에서 얘기한 @Transactional 추가 위치에 따른 처리 차이에 대한 내용이다.
메서드에 추가한 경우: 해당 메서드만 트랜잭션 처리
클래스에 추가한 경우: 모든 메서드의 트랜잭션 처리
@Transactional 사용시 주의점
다음 내용은 최범균님의 '프로그래밍 초식 : 초심자가 저지르기 쉬운 DB 코딩 실수 3가지' 에서 나온 내용이다.
다음과 같은 코드가 있다.
1번
@Transactional public void registerMembers(List<Req> reqs) { reqs.forEach(req -> { try { registerService.register(req); logDao.insert(createSuccessLog(req, ex)); } catch (Exception ex) { logDao.insert(createFailLog(req, ex)); } }); }
2번
@Transactional public void register(Req req) { anyRepository.save(toData(req)); if (check()) { throw new RuntimeException("!"); } }
위 '1번'처럼 @Transactional 안에 try catch를 하게 되면
의도한 대로 동작이 안되게 될 수 있다.
loop를 돌면서 register 메서드를 호출하게 되는데
Exception이 발생하지 않으면 아무 문제가 없는 코드이지만
register 에서 Exception이 발생하게 되면
throw new RuntimeException("!"); 을 호출하면서 트랜잭션이 '롤백' 상태로 바뀌게 된다.
이 상태가 되면 나머지 register와 insert는 롤백 상태에서 진행하게 되기 때문에
아무 의미가 없는 상태가 되어 버린다.
그 후 forEach 문이 끝날때 커밋을 시도하게 되는데 지금 트랜잭션 상태가 롤백 상태이기 때문에
커밋을 할 수가 없고, 커밋에 실패하게 된다.
여기서 의도는 forEach 를 하면서 정상적인 수행과 실패가 난 사항들을 디비에 따로따로 기록하는 것이 의도였는데,
@Transactional 이 있다보니 의도대로 동작하지 않는 것을 알 수 있다.
이 케이스는 중첩된 @Transactional과 try-catch에 대한 내용인데,
메서드에 @Transactional 설정하고, 안에서 try-catch로 Exception을 처리하고 전파하지 않으면
다음과 같은 내용이 확인 필요하다.
1. 중첩된 @Transactional이 있는지
2. 의도하지 않은 롤백 가능성이 없는지
3. 의도한 롤백이 안될 가능성은 없는지
그래서 위 케이스에서는 의도한대로 트랜잭션이 동작하는지 테스트가 필요하다.
@Transactional 분리 사용
TBD
출처: https://morioh.com/p/a97ace580213
출처: https://goddaehee.tistory.com/167
출처: https://www.youtube.com/watch?v=n85UzIReFjY&t=313s&ab_channel=%EC%B5%9C%EB%B2%94%EA%B7%A0
'Spring' 카테고리의 다른 글
JPA Hibernate Proxy (1) 2022.09.13 Spring API 공통 response 포맷 개발하기 (1) 2022.07.10