스프링의 전통적인 트랜잭션
스프링 서버 구동: 톰캣 시작 → 서버 작동 → web.xml 확인 → context.xml 확인, DB연결 테스트
요청 처리 순서
① 송금 요청 수신
② 요청이 web.xml을 거치며 DB 연결 세션 생성
③ 요청이 필터를 거치며 JDBC 커넥션 시작 → 트랜잭션 시작 → 영속성 컨텍스트 시작
④ 요청 분기를 확인 후, 요청에 맞는 서비스 호출
⑤ 송금 서비스가 홍길동 SELECT, 장보고 SELECT
⑥ 영속성 컨텍스트에 객체 생성(1차 캐시에 엔티티 생성)
⑦ Repository를 통해 데이터를 들고와 값을 변경
⑧ 업데이트 결과 반환
⑨ JDBC 커넥션 종료 → 트랜잭션 종료(commit → 영속성 컨텍스트 변경감지, 데이터가 바뀌었다면 flush) → 영속성 컨텍스트 종료 → 응답 반환(Controller라면 html, RestController라면 데이터 반환) → DB연결 세션 종료
기존 스프링의 트랜잭션 개선과 JPA의 OSIV 전략
스프링부트는 자원 소모 감축을 위해 두가지 과정을 수정하였다.
1.
응답과정⑨에서 JDBC, 트랜잭션, 영속성 컨텍스트를 종료시키는것 보다 Service-Controller과정⑧에서 종료시키는것이 더욱 효율적 - DB 커넥션 시간과 영속성 컨텍스트 지속시간이 줄고 트랜잭션 범위가 적어진다.
2.
필터를 거치며③ JDBC, 트랜잭션을 시작하는것 보다 Controller-Service과정④에서 시작하는것이 더욱 효율적 - DB 커넥션 시간과 영속성 컨텍스트 지속시간이 줄고 트랜잭션 범위가 적어진다.
여기서 문제점이 생기는데, 가져온 엔티티가 FK를 가지고, FetchType.LAZY의 경우일때이다. 기본적으로 지연로딩을 사용하며 DB에서 영속성 컨텍스트로 값을 가져올땐 연관객체가 실제 DB에서 가져온 데이터가 아닌 프록시, 비유하자면 빈 객체의 형태로 붙게 된다. 컨트롤러가 연관객체를 사용할것인지 모르기 때문에 “필요하다면 로딩” 전략이 지연로딩의 전략이다.
컨트롤러가 연관객체를 필요로 한다면, 개선안에의 컨트롤러에서는 영속성 컨텍스트가 이미 종료된 다음 시점이다. 따라서 프록시 객체를 호출해도, 컨텍스트 내의 프록시는 다시 볼 수 없게 된다. 이 말인 즉슨, 지연로딩을 사용할 수 없게 된다.
이를 개선하여 영속성 컨텍스트의 시작과 종료만을 서블릿 필터로 당기는것이다. 이 전략을 OSIV(Open Session In View)이라고 부른다. 컨트롤러에서 지연로딩으로 연관객체를 사용하면, 영속성 컨텍스트 내부의 프록시가 호출이 되고 연결이 끊어졌던 JDBC를 다시 연결하여 정보를 들고온 후 곧바로 연결을 끊는다. 이 과정은 물론 SELECT로직만 가능하다.(read-only)
이 속성은 application.yml의 jpa.open-in-view에서 true로 바꾸어 설정 가능하다. false로 설정한다면 Controller단계에서의 Lazy Loading은 작동하지 않는다. 스프링부트 2.0부터는 OSIV 활성상태일때 경고를 발행한다.
스프링부트의 트랜잭션 - JPA OSIV 전략
스프링 서버 구동: 톰캣 시작 → 서버 작동 → web.xml 확인 → context.xml 확인, DB연결 테스트
요청 처리 순서
① 송금 요청 수신
② 요청이 web.xml을 거치며 DB 연결 세션 생성
③ 요청이 필터를 거치며 영속성 컨텍스트 시작
④ 요청 분기를 확인 후, 요청에 맞는 서비스 호출, JDBC 커넥션 시작 → 트랜잭션 시작
⑤ 송금 서비스가 홍길동 SELECT, 장보고 SELECT
⑥ 영속성 컨텍스트에 객체 생성(1차 캐시에 엔티티 생성)
⑦ Repository를 통해 데이터를 들고와 값을 변경
⑧ 업데이트 결과 반환, JDBC 커넥션 종료 → 트랜잭션 종료(commit → 영속성 컨텍스트 변경감지, 데이터가 바뀌었다면 flush)
⑨ 영속성 컨텍스트 종료 → 응답 반환(Controller라면 html, RestController라면 데이터 반환) → DB연결 세션 종료
•
⑧, ⑨의 사이에 Lazy Loading을 실행한다면, 프록시 접근 → JDBC 커넥션 연결 → 연관객체 SELECT, 데이터 반환 → JDBC 커넥션 종료