9. 스프링의 전통적인 트랜잭션

스프링이 시작되면

  1. 톰캣 시작
  2. web.xml 읽음
  3. context.xml 읽고 DB 연결테스트되면 세팅 끝!

이러고 나서 요청이 들어오면,
web.xml -> 필터 -> 스프링컨테이너[컨트롤러-서비스-레파지토리] -> 영속성컨텍스트 -> DB

전통적인 방식 (모두가 다같이 처음에 시작!)

web.xml과 필터를 거치게되면…

  1. DB 연결 세션 생성(JDBC가 연결됨, CRUD가능)
  2. 트랜잭션이 시작.
  3. 영속성 컨텍스트 시작.

스프링 컨테이너로 오게되면…

  1. 컨트롤러에서 요청을 분기하고 요청에 맞는 서비스를 요청
  2. 서비스는 해당 트랜잭션들을 JDBC레파지토리를 통해 실행.
  3. 레파지토리의 메소드에 따라 영속성 컨텍스트에서 DB엔티티를 객체화해서 가져옴
  4. 객체화된 데이터를 서비스에서 수정하고 컨트롤러에 결과를 전달.
  5. 컨트롤러는 json이나 html을 반환
    • 이때 트랜잭션이 종료된다.
    • 영속성 컨텍스트는 값의 변경됨을 인식하고 DB에 flush해서 변경된 값으로 업데이트.
    • 영속성 컨텍스트 종료
    • DB연결 세션을 종료.

기존방식 개산안과 그 문제점

기존의 방식이 너무 쓸데없이 오래걸린다. 개선해보자.

기존방식 개선해볼까(문제가 있음.)

기존에는 컨트롤러로 요청이 넘어가기전에
DB 연결 세션 생성 / 트랜잭션이 시작 됐는데,
이걸 그냥 서비스가 호출될 때 실행하도록 개선

또, 컨트롤러가 종료될 때
트랜잭션 종료 / DB연결 세션 종료 / DB flush 및 영속성 종료 됐는데,
이걸 그냥 서비스가 종료될 때 실행되도록 개선

하지만 이렇게 하면 문제가 발생한다!
만약 DB에서 MANY TO ONE 관계에서 MANY side에 있는 데이터를 가져오려고 할 때,
영속성 컨텍스트는 해당 값을 DB에서 가져올 때 EAGER 전략을 취하면(디폴트 전략이 EAGER),
MANY side에 해당하는 데이터 뿐만 아니라 ONE side에 해당하는 자료도 가져온다.
그래서 데이터가 컨트롤러에 전달되서 영속성 컨텍스트가 종료되어도,
MANY side 데이터나, ONE side 데이터 모두 다룰 수 있다.

만약 LAZY 전략으로 변경하면, ONE side에 해당하는 데이터를 가져오지 않는다.
ONE side 데이터의 프록시 객체를 가져온다.(그냥 연결된 빈 객체다.)
그래서 컨트롤러에 전달된 데이터는 MANY side 데이터랑 프록시 데이터 뿐이라,
ONE side의 데이터를 다룰 수 없다.
(게다가 컨트롤러에 전달될 때 영속성 컨텍스트가 종료되서 DB에 요청도 못한다.)

OSIV (open session in view, session에 영속성 컨텍스트)

아하! 이 문제를 해결하기 위해서 영속성 컨텍스트는 원래대로 컨트롤러가 반환할 때 종료하자!
영속성 컨텍스트가 살아있다면, 프록시를 통해 JDBC, 트랜잭션이 종료됐었어도 스스로 JDBC 커넥션을 시작해서
프록시에 해당하는 데이터를 가져올 수 있다!(그러고나서 JDBC 커넥션을 닫는다!)
하지만 여전히 업데이트는 불가능…(트랜잭션이 종료되서…)

1
hibernate.enable_lazy_load_no_trans: true  

application.yml에 우리가 이렇게 쓰면 문제점을 고친 방식으로 활용 가능하다.(디폴트)
하지만 false로 하면 Lazy Loading이 불가능한 방식으로 작동한다.

정리하자면…

영속성 컨텍스트는 컨트롤러가 작동하면서 생성 및 종료!!
트랜잭션과 JDBC 커넥션은 서비스가 작동하면서 생성 및 종료!

Lazy Loading

지금까지 살펴본 일들은, 다 Lazy Loading을 처리하기 위한 일들이다.
Lazy Loading은 Eager와 달리, 지금 요청한건 가져오고, 얘랑 관련된건 나중에… 이런 느낌이다.
그래서 프록시를 대신해서 가져오고, 프록시를 통해 관련된 데이터를 가져오라고 할 수 있다.(영속성 컨텍스트에 살아있을 때)

즉 영속성 컨텍스트를 언제 생성하고 언제 종료 시킬 것이냐에 대한 포스트였다.

Share