Java/JPA

JPA실전 2 - OSIV와 성능

amungstudy 2023. 12. 12. 13:56

Open Session In View: 하이버네이트, Open EntityManager In View: JPA
(관례상 OSIV라 한다.)

 

spring.jpa.open-in-view : true 기본값

 

OSIV 전략은 트랜잭션 시작처럼 최초 데이터베이스 커넥션 시작 시점부터 API 응답이 끝날 때 까지 영속성 컨텍스트
와 데이터베이스 커넥션을 유지한다. 이를 통해 지연로딩이 가능하지만, 너무 오랜시간동안 데이터베이스 커넥션 리소스를 사용하기 때문에, 실시간 트래픽이 중요한 애플리케이션에서는 커넥션이 모자랄 수 있다. 이것은 결국 장애로 이어진다

 

spring.jpa.open-in-view: false OSIV 종료

 

 open-in-view: false 로 해야 커넥션풀이 마르지 않음.

 

OSIV를 끄면 커넥션 리소스를 낭비하지 않는다. 그러나 모든 지연로딩을 트랜잭션 안에서 처리해야한다. 따라서 지연로딩 코드를 트랜잭션 안으로 넣어야한다. 


권장사항:

 

    트래픽이 많은 api서버(대규모)는 OSIV를 끈다.
    admin같은곳에서는 OSIV를 켠다.

대신 커멘드와 쿼리를 분리한다.

크고 복잡한 애플리케이션을 개발한다면 분리하는것이 유지보수의 관점에서 좋다.   

 

예시)

OrderService

  • OrderService: 핵심 비즈니스 로직
  • OrderQueryService: 화면이나 API에 맞춘 서비스 (주로 읽기 전용 트랜잭션 사용)

 이런식으로 컨트롤러에서 지연로딩하는 코드를 따로 Query용 QueryService로 만들어주고
  컨트롤러에서는 리턴만 한다.

보통 서비스계층에서 트랜잭션을 유지하므로 두 서비스 모두 트랜잭션을 유지하면서 지연로딩을 사용할 수 있다.

 

컨트롤러의

@GetMapping("/api/v3/orders")
public List<OrderDto> ordersV3(){
   List<Order> orders = orderRepository.findAllWithItem();
    List<OrderDto> collect = orders.stream()
            .map(o -> new OrderDto(o))
            .collect(toList());
    return collect;
}

이 코드를

@Service
@Transactional(readOnly = true)
public class OrderQueryService {

    public List<OrderApiController.OrderDto> ordersV3(){
        List<Order> orders = orderRepository.findAllWithItem();
        List<OrderApiController.OrderDto> collect = orders.stream()
                .map(o -> new OrderApiController.OrderDto(o))
                .collect(toList());
        return collect;
    }
}

이런식으로 따로 서비스를 만들어서 작성한다. 

 

REFERENCE : 인프런 김영한 JPA실전2편 강의