• 문제상황
    • Controller -> Service -> Service(@Transactional) 
  • 문제이유
    • 컨트롤러에 디폴트로 Transaction이 걸려있다고 생각했다.(예전 osiv를 생각했던것임)
    • 그렇지만 현재의 스프링 osiv는 서비스단에 직접 트랜잭션을 명시해야 트랜잭션이 시작됨.
    • 최종적으로 호출되는 Service에는 @Transactional을 명시했는데? 왜 안된거지?
      • 같은 서비스 클래스 내에서의 호출(Service->Service(@Transactional)을 했기때문에 transaction이 전파? 되지않았다..
  • 문제해결
    • controller단에만 @transactional 명시
      • 예전 transaction 적용방법. 컨트롤러단에서 데이터를 변경하면 의도치않은 데이터 변경값이 뷰에 전달될가능성이 커서 안좋은 방법이다!! PASS!!
    • service단에 @transactional 명시
      • 외부에서 쓰일 메소드는 transactional 명시해놓음
      • 내부에서만 쓰일 메소드는 private로 명시함으로써 "이 메소드는 내부에서만 쓰일거고, 내부에서 호출은 transaction이 적용이 안돼!" 라는 것을 인지할수 있다. @Transactional 붙여보면 컴파일시점에서부터 에러가 남을 알수있음!

 

  • 트랜잭션 범위의 영속성 컨텍스트 전략이 기본이다

  • 트랜잭션범위(=영속성 컨텍스트 범)에 있지않는 뷰 레벨(컨트롤러 등)에서 엔티티에 접근을 하다가 LazyInitailize Exception 이 발생!

  • 원인 사례 : 

    • " 어 나는 뷰에는 이 엔티티의 필드를 좀 암호화해서 넘기구싶어" 것을 원할수가 있음

    • " 뷰 레벨에서 나중에 (Lazy) 꺼내 써야지~ 지금은 당장 필요없으니깐~"

  • Lazy InitializeException을 해결하는 방법이 뭐가 있을까?

    • 뷰 레벨전에 엔티티를 미리 초기화하는 방법이 있겠네

      • 그럼 뷰 레벨전에 엔티티를 초기화하는 방법엔 뭐가있을까?

        • 글로벌 페치 전략 수정

          • Entity에 fetchType Eager로 지정

          • 사용하지 않을수도있는 엔티티 까지도 로딩해서 메모리 문제.. 여기까진 OK

          • 일반 JPA 메소드로 조회했을떄는 이너조인해서 잘 가져온다 문제가없다.. 근데? JPQL을 사용하게 되면 N+1 문제가 발생한다

        • JPQL 페치 조인

          • 위의 N+1문제를 해결하기 위해 JPQL을 쓸때는 페치 조인을 쓴다. 

        • 강제로 초기화(Facade)

          • 이것도 아니면 Lazy 로딩인걸 무조건 initialize하는 계층인 facade 계층을 하나 만든다.(Controller <-> facade <-> service)

    • 뷰 레벨전에 엔티티를 미리 초기화의 문제점

      • 누가 뷰 만들때 이 엔티티가 초기화 잘됐는지까지 확인해.. 그냥 뷰만 딱 만들지..너무 모델뷰간의 의존성이 높아진다..

    • 그러면 Lazy InitializeException 가장 좋은방법은 OSIV 적용!

      • 스프링의 예전 OSIV

        • 요청당 트랜잭션 방식의 OSIV , 즉 요청이 들어오는 즉시(필터/인터셉터부터) 트랜잭션 시작!  영속성컨텍스트도 똑같이 시작!

          • 문제점 : 뷰레벨에서 모델에 셋을할수있음. 즉 데이터가 바뀔수있음 뷰레벨에서.

      • 지금의 스프링 OSIV

        • 서비스단에서 트랜잭션 시작!  영속성컨텍스트는 필터/인터셉터부터 시작

+ Recent posts