ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [JPA] 알쓸지
    카테고리 없음 2024. 2. 28. 12:17

    Named Query

    Application Loading 시점에 파싱을 해서 들고 있는 과정에서 Query 검증이 가능하다.
    Mybatis를 사용할 때, 쿼리의 검증을 Test를 통해서 하거나, 실제로 Controller API를 찔러서 확인했었을 때가 있었는데, 이와 같은 Query 문법오류를 방지할 수 있어서 굉장한 듯 하다.

     

    벌크연산

    JPQL로 insert, update, delete를 날리면 DB에 직접 반영되기 때문에 영속성 컨텍스트초기화 해주어야 오류가 없다.
    영속성 컨텍스트를 초기화 후, 재조회 하여야 DB의 데이터와 일관성 있는 Entity를 사용 가능하다.

     

    Fetch Join 최적화 (대부분의 성능 문제 해결)

    Repository에는 순수한 Entity만을 조회하는 용도의 메서드만 생성
    Entity Graph 탐색을 할 수 있기 때문에 재사용 가능
    Repository에서 Dto로 변환(Endpoint 와 직접적인 영향)해서 반환하는 메서드는 Repository의 하위 패키지에 별도로 만들어서 성능최적화 용도로 작성

     

    Fetch Join 뻥튀기 문제

    DB의 Join의 경우 Graph하게 데이터를 나타낼 수 없고, Row단위로 Column의 데이터가 중복으로 나올 수 밖에 없다.

    ex) 회원 - 주문 - 상품의 관계
    Graph

    회원1 - 주문1 - 상품 1
                  - 상품 2
          - 주문2 - 상품 3
                  - 상품 4

    DataBase

    회원1 - 주문1 - 상품 1
    회원1 - 주문1 - 상품 2
    회원1 - 주문2 - 상품 3
    회원1 - 주문1 - 상품 4

    그래서 JPQL에서 distinct를 사용하지 않으면 DataBase의 Row 갯수와 같이 List 개수를 반환한다.
    그러나 우리가 생각하는 Graph구조에 해당하는 값을 얻으려면 distinct를 사용해야함.

    SQL에 distinct가 붙어서 날라가는 결과여서 Column이 전부 다 같지 않은 이상 DataBase의 Row가 줄어드는 일은 없으나
    JPQL distinct Entity의 중복을 한번 걸러주는 작업을 해주기 때문에 우리가 원하는 결과를 얻을 수 있다.

    ※ 주의
    - 이러한 조인의 결과 개수는 1 : N 중, N의 개수가 반환되기 때문에 페이징 처리가 불가
    Paging 시, Warning Memory에 모든 Row의 개수를 올려서 페이징을 해버린다.

    ※ Collection Fatch Join은 1개만 해야한다!!!
    1 : N : M의 경우 가장 마지막 M의 개수에 대해 default_batch_fetch_size를 항상 글로벌적으로 적용해서 N + 1문제를 방지하자 (https://vladmihalcea.com/hibernate-multiplebagfetchexception/)
    1 : N에 대해서만 써야한다.

    ※ 즉 ToOne은 Row가 뻥튀기 되지 않기 때문에 Fetch Join으로 가져오고, Collection Fetch Join은 N + 1문제를 해결하기위해 IN Query ( default_batch_fetch_size 설정 혹은 @BatchSize ) 로 한방에 여러개를 가져오는 것으로 최적화 하자.

    Fetch Join으로 중복을 감안하고 네트워크 데이터량이 늘어나더라도 네트워크를 한번만 타고 Row를 다 가져와서 JPA에서 그루핑 해주는 것을 하느냐
    Batch Fetch로 IN 쿼리로 중복없는 Row를 가져오고 최적화된 네트워크 데이터량으로 여러번 네트워크를 타느냐 Trade Off

    batch_fetch_size의 적당한 값은 100 ~ 1000개, DB와 WAS가 순간부하를 견딜 수 있는 값이 적당하다. 500개가 적당할 듯

     

    트래픽이 많을 경우 캐시

    Entity, Dto Query로 가능하지 않은 경우 Redis나 Local Cache를 사용하여 해야하는데 이 때, Entity를 캐시하면 절대 안되고 Dto로 해야한다. 영속성 Entity를 참조하고 있거나 이러면 꼬일 수 있으니 

     

    Open-Session-InView

    영속성 컨텍스트가 사라지는 시점 조절

    트랜잭션이 있는 범위에서만 동작할 것이냐? (false), Api가 응답 할 때 까지 살아 있을 것이냐 (true)

    Command, Query Service를 분리해서 작성하면 좋음
    Query Service는 핵심 비지니스가 아닌, 화면에 종속된 값들을 @Transactional 으로 지연로딩까지 다 해결한 후 반환하는 로직이 주를 이룰 것임

    open-in-view 는 트래픽이 많을 경우에는 Service 안에서 Transacion이 존재하는 곳에서만 영속성이 유지되도록 하는것이 좋다. Service - Repository 안에서 Connection을 사용하고 반환하기 때문에 길게 유지하지 않고 바로바로 반환하기 때문

    admin 처럼 Connection이 많지 않은 경우에는 Controller에서 개발 편의를 위해 지연로딩을 이용한 반환도 좋다

    QueryDSL

    PageableExecutionUtils.getPage
    countQuery를 실행하지 않는 경우
    1. pageSize보다 contentSize가 적을 경우
        - contentSize가 totalCount
    2. 마지막 offset에서 pageSize보다 contentSize가 적을 경우
        - offset + contentSize 로 totalCount를 계산

     

    @Convert

    Table Column에 문자열로 Json을 저장할 때 사용했었는데 

    @Embedded @Embedable 와는 달리 @EqualsAndHashCode를 넣어야한다.
    참고: https://jojoldu.tistory.com/536

    댓글

Designed by Tistory.