스프링 프레임워크, 컨테이너와 OSIV

dropKick·2020년 9월 15일
0
post-thumbnail

스프링 컨테이너의 엔티티 접근

  • Open Session In View(Hibernate)
  • Open JPA In View(JPA)

기본적으로 스프링 컨테이너의 객체가 Entity Manager, em의 컨텍스트의 접근하는 것은 트랜잭션 범위 내에서 가능하다.
트랜잭션이 종료되면 더 이상 엔티티에 접근할 수 없는데 어떻게 지연 로딩을 사용하는
로직들에서 에러가 발생하지 않는걸까?

지연로딩은 트랜잭션 시작 시점에서 엔티티를 불러오지 않고 실제 데이터에 접근하게 되는 시점에 엔티티를 불러옴

OSIV가 작동할 때

  • 트랜잭션의 범위는 엔티티에 접근이 가능한 Service, Repository에 한정
  • 하지만 Controller를 통해 데이터를 반환해주기 위해선 persistent context가 살아있어야 한다
    (지연 로딩이 있을 경우 커넥션이 유지되어야 로딩이 가능)
  • OSIV는 트랜잭션이 종료 되더라도 커넥션을 유지하고 지연 로딩에 의한 동작이 가능하게 된다

이는 지연 로딩을 사용할 수 있게 만들지만 Response Body나 Form에 데이터가 담겨
사용자에게 실제로 반환될 때까지 커넥션을 유지하기 때문에 만약 외부 API 호출에 의한 블로킹이 걸린다거나 하면 커넥션 풀이 모두 차버리는 문제가 발생할 수 있다.

OSIV가 작동하지 않을 때

  • 트랜잭션 종료에 따라 커넥션도 반납
  • 트랜잭션이 종료되면 커넥션을 반납하기 때문에 트랜잭션 범위 내에서 지연로딩까지 처리해야 한다
    (정확히는 엔티티를 반환받아 객체에 담기는 시점)
org.hibernate.LazyInitializationException: could not initialize proxy [jpabook.jpashop.domain.Member#1] - no Session
	at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:169) ~[hibernate-core-5.3.17.Final.jar:5.3.17.Final]
	at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:309) ~[hibernate-core-5.3.17.Final.jar:5.3.17.Final]
	at org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor.intercept(ByteBuddyInterceptor.java:45) ~[hibernate-core-5.3.17.Final.jar:5.3.17.Final]
	at org.hibernate.proxy.ProxyConfiguration$InterceptorDispatcher.intercept(ProxyConfiguration.java:95) ~[hibernate-core-5.3.17.Final.jar:5.3.17.Final]
	at jpabook.jpashop.domain.Member$HibernateProxy$X70B1PVb.getName(Unknown Source) ~[main/:na]
	at jpabook.jpashop.api.OrderApiController.ordersV1(OrderApiController.java:35) ~[main/:na]
@GetMapping("/api/v1/orders")
public List<Order> ordersV1() {
        List<Order> all = orderRepository.findAllByCriteria(new OrderSearch());
        for (Order order : all) {
            order.getMember().getName(); // OSIV : false에 따른 no session 에러 발생
  • 지연 로딩이 이루어지는 부분에서 에러 발생

OSIV 사용하지 않고 최적화 시키기

  • 컨트롤러에 있는 비즈니스 로직을 별도의 서비스로 분리하기
    기존 컨트롤러에 있는 비즈니스 로직과 DTO를 별도의 서비스로 분리한다
  • 비즈니스 로직 자체를 분리하기
    엔티티 자체를 관리하는 핵심적인 비즈니스 로직과 성능 최적화가 필요한 조회 로직을 별도로 분리한다
    따라서 OrderService(핵심)과 OrderQuerySerivce(조회)로 분리

결론

  • 클라이언트의 연결이 많아 커넥션 풀의 관리가 필요한 경우
    OSIV를 사용하지 않고 컨트롤러와 서비스를 분리한다.
  • 커넥션 풀을 따로 관리할 필요가 없는 경우
    OSIV를 사용하여 비즈니스 로직 작성을 편리하게 한다.

0개의 댓글