-
트랜잭션 (feat. @Transactional)Spring 2021. 12. 18. 18:18
트랜잭션이란 비지니스 로직에서 쪼개질 수 없는 하나 단위의 작업을 말한다.
(DB상태 변화를 위해 행하는 작업의 작은 단위)
ACID원칙
트랜잭션에는 네가지 원칙이 있다.
- 원자성 (Atomicity) : 트랜잭션은 하나의 단위로 이루어져야 한다. (모두 성공시 실행, 하나라도 실패시 rollback처리)
- 일관성 (Consistency) : 트랜잭션 성공시 DB의 데이터는 일관성(같은 결과)을 지녀야 한다.
- 격리성 (Isolation) : 트랜잭션 처리 과정 중 외부의 간섭은 없어야 한다. (독립적 실행)
- 영속성 (Durability) : 트랜잭션이 성공하면 그 결과는 영속적으로 보관 되어야 한다. (영구 반영)
트랜잭션 관리 (범위)
트랜잭션의 경계는 프레젠테이션층과 비지니스 로직 층 사이에 존재하는 것이 일반적이다. (서비스 층에서 트랜잭션 시작)
스프링에서의 트랜잭션 설정
스프링에서의 트랜잭션 설정은 @Transactional 어노테이션을 이용하여 완료할 수 있다. 클래스, 인터페이스, 메서드에 적용할 수 있으면 메서드에 가까울수록 우선순위를 지닌다. 적용된 범위에서는 트랜잭션 기능이 포함된 프록시 객체가 생성되어 성공여부에 따라서 commit 혹은 rollback을 결정한다.
readOnly 옵션
readOnly 옵션은 해당 트랜잭션을 읽기 전용으로 설정하여 쓰기 작업의 진행을 막아준다. 쓰기작업을 실행하지 않는 트랜잭션에 @Transactional(readOnly=true) 옵션을 주게되면 하이버네이트의 Session Flush Mode를 MANUAL로 설정하여 강제로 플러시를 호출하지 않는 한 플러시가 일어나지 않아 변경감지를 위한 스냅샷을 보관하지 않아 성능이 향상될 수 있다.
private(+ private inner method), final 메서드에서의 @Transactional
@Service @Transactional(readOnly = true) public class ActivityService { public void create(ActivityRequest activityRequest, MultipartFile multipartFile) throws IOException { saveImage(activity, multipartFile); } @Transactional private void saveImage(Activity activity, MultipartFile multipartFile) throws IOException { } }
@Transactional 어노테이션은 private 메서드에서 작동하지 않는다. 스프링은 트랜잭션 처리를 위해 빈 객체에 타깃 클래스를 상속해서 프록시 객체를 생성하기 때문에 private메서드의 경우 상속이 불가능하여 @Transactional 어노테이션이 적용되지 않는다. 같은 이유로 final 메서드도 적용되지 않는다.
위의 코드에서 스프링 프록시는 프록시를 통해 들어온 외부 메서드의 호출에서만 가로챌 수 있기 때문에 내부 메서드의 @Transactional 어노테이션을 알 수 없다. 따라서 위의 코드에서는 트랜잭션이 의도한대로 작동하지 않을 것이다.
만약, private 메서드에 트랜잭션 처리를 하고 싶다면 AspectJ를 이용한 방법을 사용할 수 있다.
@Service @Transactional(readOnly = true) public class ActivityService { @Transactional public void create(ActivityRequest activityRequest, MultipartFile multipartFile) throws IOException { saveImage(activity, multipartFile); } private void saveImage(Activity activity, MultipartFile multipartFile) throws IOException { } }
위의 코드와 같이 해당 private메서드를 부르는 public 메서드에 @Transactional 어노테이션을 붙여준다면 같은 트랜잭션 안에서 private 메서드를 부르고 이는 한 트랜잭션 안에서 이뤄지는 변화이므로 의도한대로 메서드를 호출하고 작동하게 된다.
https://stackoverflow.com/questions/45630211/spring-transaction-when-calling-private-method
(참고한 사이트)
https://qteveryday.tistory.com/81
https://willseungh0.tistory.com/73
'Spring' 카테고리의 다른 글
Lombok 사용시 주의할 점 (0) 2021.12.29 RESTful API (2) 2021.12.19 연관 관계의 엔티티 기준으로 order 해서 데이터를 가져오고 싶을 때 (0) 2021.11.06 multipart.MaxUploadSizeExceededException 오류 발생시 (0) 2021.11.06 Data too long for column 오류 발생시 (0) 2021.11.06