- Spring Boot의 OSIV 설정은 default로 true이다.
→ 트랜잭션이 끝나도 영속성 컨텍스트가 계속 유지된다.
동작 원리
- 요청이 들어오면 서블릿 필터나 인터셉터에서 영속성 컨텍스트를 생성한다. (트랜잭션은 없다.)
- 서비스단에서 로직이 시작되면 트랜잭션을 생성한다. -
@Transactional
(영속성 컨텍스트 유지)
- 서비스단의 로직이 끝나면 트랜잭션을 커밋하고, 영속성 컨텍스트를 flush한다.
→ 트랜잭션은 종료되고, 영속성 컨텍스트는 유지된다. 다만, flush는 이미 일어난 상태이다.
- 컨트롤러와 뷰까지 영속성 컨텍스트가 유지되어 엔티티는 계속 영속 상태이다.
→ 계속 영속 상태이므로 컨트롤러, 뷰 단에서 지연 로딩이 가능하다.
- 요청이 모두 끝나면 영속성 컨텍스트를 종료한다.
→ 다만, flush는 하지않기때문에 변경 감지는 하지못한다. (바로 em.close()를 한다.)
→ 만약 강제로 presetation layer에서 flush를 할 경우, 트랜잭션 범위 밖이므로 데이터를 수정할 수 없다는 예외가 발생한다. javax.persistence.TransactionRequiredException
⇒ 트랜잭션과 영속성 컨텍스트 2가지로 나누어 생각할 수 있다. flush는 트랜잭션의 문제이고, 영속 상태는 영속성 컨텍스트의 문제이다.
주의점
- 컨트롤러 및 뷰단까지 영속성 컨텍스트가 유지되기때문에, 그 시간만큼 영속성 컨텍스트가 커넥션을 물고있다.
→ 컨트롤러에서 시간이 오래 걸리는 외부 api를 사용하면 외부 api가 모두 동작할때까지 커넥션을 문채로 기다려야한다.
- 프레젠테이션 단에서 엔티티가 변경된 후에, 서비스단에서 로직이 실행되면 원치않은 엔티티 수정이 발생할 수 있다.
해결책
OSIV를 꺼서 DB 커넥션을 아낀 후에, Command와 Query를 분리한다. CQRS
OrderService라는 것이 있으면 이 서비스단을 아래와 같이 분리한다.
- OrderService (Command) : 핵심 비지니스 로직 (Create, Update, Delete 등의 로직)
- OrderQueryService (Query) : 데이터 Get (주로 읽기 전용 트랜잭션 사용)