Search

OSIV 에 관하여

EntityManager 생성 시점

트랜잭션 시작 시
JpaTransactionManager
doBegin()
createEntityManagerForTransaction() 으로 새로운 EM 생성 요청
createEntityManagerForTransaction()
obtainEntityManagerFactory() 로 EMF 획득(필드로 가지고 있음)
EntityManagerFactoryInfo 의 createNativeEntityManager() 호출
AbstractEntityManagerFactoryBean(EntityManagerFactoryInfo 추상 구현체)
createNativeEntityManager()
SessionFactoryImpl 의 createEntityManager() 호출
SessionFactoryImpl
createEntityManager()
openSession() 호출
openSession()
new SessionImpl(sessionFactory, this);
SessionImpl
생성자
영속성 컨텍스트, 액션 큐 등 생성

EntityManager 라이프사이클

open-in-view 가 true 라면
DispatcherServlet 의 doDispatch 메서드에서
ha.handle() 한 뒤(실제 컨트롤러 부르는 메서드)
processDispatchResult() 메서드를 호출하여 close 를 한다.
false 라면
APTM 의 commit() 메서드 에서 processCommit() 호출
processCommit() 에서 cleanupAfterCompletion() 호출
cleanupAfterCompletion() 에서 JTM 의 doCleanupAfterCompletion() 호출
JTM 의 doCleanupAfterCompletion() 에서 EntityManagerFactoryUtils 의 closeEntityManager() 메서드를 호출하여 close
그렇다면 이 둘의 차이는 어디서 발생하는가?
일단 true 면 HandlerInterceptor 로 WebRequestHandlerInterceptorAdapter 가 추가로 등록된다.
여기서 close 를 한다.
그렇다면 true 일 때는 왜 APTM 의 commit 메서드에서 processCommit() 을 호출하지 않는가?
APTM 의 cleanupAfterCompletion 메서드에서 JTM 의 doCleanupAfterCompletion() 을 호출하기 위해서는 if 분기문의 status.isNewTransaction() 이 true 가 되어야 하는데 여기서 통과하지 못했다.
즉 Service 계층의 Transactional 어노테이션으로 트랜잭션을 열기 전 이미 트랜잭션이 열려 있는 상태인 것.
그랬다. 이미 트랜잭션을 열어버린 것이다. 그럼 트랜잭션을 연 가장 유력한 용의자는 WebRequestHandlerInterceptorAdapter 인 것 같았다. interceptor 라서 preHandle 이 있다. 여길 디버그 찍어보았다.
DispatcherServlet 에서 핸들러와 핸들러어댑터를 찾고 핸들러어댑터에 handle 을 호출하기 전 preHandle 로 인터셉터를 호출한다.
WebRequestHandlerInterceptorAdapter 의 preHandle 에서 필드로 등록된 requestInterceptor 의 preHandle 을 요청한다.
등록된 requestInterceptor 는 OpenEntityManagerInViewInterceptor 이고 이 객체의 preHandle 메서드에서 EMF 를 얻은 뒤 createEntityManager 로 EM 을 생성한다.
이때 커넥션은 빌려가지 않음! 단순히 세션을 열과 em 을 생성할 뿐

OSIV 의 기능

true 일 때
Lazy 일 때
프록시로 있는 객체 조회 가능(쿼리 발생)
프록시로 있는 객체 변경 불가(더티 체킹 불가능) 하지만 에러 X
false 일 때
Lazy 일 때
프록시로 있는 객체 조회 시 LazyInitializationException: could not initialize proxy [com.example.tosstemp.Team#1] - no Session] with root cause 에러 발생
변경도 당연히 불가

결론

true 일 때는 트랜잭셔널 어노테이션 바깥에서도 em이 열려있다. 하지만 준영속상태이다. 그래서 조회는 가능하다. 하지만 트랜잭셔널 어노테이션 바깥이기때문에 더티체킹을 하지 않는다. 그래서 변경점에 대한 업데이트를 하지 않는다.
그 이유는 트랜잭셔널 어노테이션으로 세션을 연 것이 아니고 냅다 interceptor 에서 세션을 생성했기 때문이다.
false 일 때는 커넥션, 세션 모두 닫혀있다. 그래서 레이지 로딩 조회도 되지 않는다.
EM 생성 → 커넥션 획득 → 트랜잭션 생성 순
setAutoCommit 하면 커넥션을 뒤에서 획득하는데 이 때는?
ConnectionHandle 을 생성해서 커넥션을 실제로 얻는 것과는 다르다.
ConnectionHandle 생성은 세션만으로 가능하다.
그래서 트랜잭션 begin 호출 시 TransactionStatus 만 ACTIVE 로 바꾼다! 실제 커넥션을 획득하지 않고!