베어_
TechBear
베어_
전체 방문자
오늘
어제
  • 분류 전체보기 (336)
    • Spring (33)
      • 개념 (13)
      • Security (5)
      • 실습 (1)
      • 토비 스프링 (11)
    • JPA (6)
    • 프로젝트 기록 (24)
    • DB (13)
    • JAVA (18)
    • 알고리즘 (50)
      • 유형정리 (8)
      • Baekjoon (21)
      • LeetCode (18)
    • 디자인패턴 (0)
    • 개발서적 (79)
      • Effective Java (78)
      • 객체지향의 사실과 오해 (1)
    • 독후감 (4)
    • 보안 (2)
    • 운영체제(OS) (53)
      • 공룡책 (53)
    • 컴퓨터 네트워크 (28)
      • 컴퓨터 네트워크 하향식 접근 (23)
    • 자료구조 (1)
    • DevOps (2)
    • 앱 개발 (20)
      • 안드로이드 스튜디오 (20)

블로그 메뉴

    공지사항

    인기 글

    태그

    • 스레드
    • 데이터베이스
    • 토비스프링
    • Spring
    • 자바
    • C++
    • BFS
    • 알고리즘
    • leetcode
    • 스프링시큐리티
    • 이펙티브자바
    • 운영체제
    • 백준
    • 함수형인터페이스
    • jpa
    • dfs
    • 자바8
    • 스프링
    • java
    • 코드업

    최근 댓글

    최근 글

    티스토리

    hELLO · Designed By 정상우.
    베어_

    TechBear

    [프로젝트 기록] fetch-join을 이용해 쿼리 최적화하기
    프로젝트 기록

    [프로젝트 기록] fetch-join을 이용해 쿼리 최적화하기

    2023. 12. 3. 23:16

    프로젝트를 진행하면서 쿼리 최적화한 과정을 기록하려고 한다. SpringBoot3.x , JPA를 사용한 프로젝트이다.

    🔍 연관관계

    기본적으로 JPA를 사용하여 프로젝트를 진행하면 Delete, Update, 맵핑관계가 설정되어 있는 조회 로직에서 쿼리를 확인하고 최적화를 진행하고 있다. 이번에는 일대다 관계를 가진 엔티티를 조회할 때의 쿼리 최적화를 진행하였다.

    @AllOpen  
    @Entity  
    class Product(  
        @ManyToOne  
        @JoinColumn    
        var category: ProductCategory,  
    
        ) {  
    
        @Id @GeneratedValue(strategy = GenerationType.IDENTITY)  
        val id: Long? = null  
    }
    @Entity  
    class ProductCategory(  
        @OneToMany(mappedBy = "category")  
        val products: MutableList<Product>  
    ) {  
    
        @Id  
        @GeneratedValue(strategy = GenerationType.IDENTITY)  
        val id: Long? = null  
    }

     

    🔍 문제의 쿼리

    검색어를 기준으로 Product를 조회하는 쿼리를 확인해보자

    fun findBySearchWord(searchWord: String): List<Product> {  
        return queryFactory  
            .selectFrom(product)  
            .where(product.productName.contains(searchWord)) 
            .limit(10)  
            .fetch()  
    }

    쿼리 로그

    쿼리가 여러개 나갔음을 확인할 수 있다. 기본적으로 @ToOne관계에서는 fetch 속성의 기본값이 Eager이기 때문에 Product를 조회하면 ProductCategory를 조회하는 쿼리가 한 번 더 나가게 된다. 기본적으로 Product를 사용하는 곳에서 ProductCategory가 항상 같이 사용된다면 fetchJoin()을 이용해서 쿼리를 최적화하는 것이 좋다.

     

    🔍 Fetch-join을 활용한 최적화

    fun findBySearchWord(searchWord: String): List<Product> {  
        return queryFactory  
            .selectFrom(product)  
            .join(product.category, productCategory).fetchJoin()  // 이 부분 추가됨
            .where(product.productName.contains(searchWord))  
            .limit(2000)  
            .fetch()  
    }

    fetch-join 쿼리

    Fetch-Join을 사용하면 쿼리가 하나만 나가기 때문에 최적화가 된다고 하는데 프로젝트가 간단해서 그런지 이를 느끼기는 쉽지 않은 것 같다.

     

    개인적으로 Fetch-Join을 쓰는 경우가 한 가지 더 있는데 트랜잭션 밖에서 관련 엔티티를 지연로딩 할 때이다. 

    https://brightmango.tistory.com/entry/OSIV-필터와-지연로딩-에러

     

    OSIV 필터와 지연로딩 에러

    🔍 관련 포스팅 OSIV와 필터 🔍 OSIV란? (Open Session In View) 스프링의 트랜잭션 범위는 Service, Repository 레이어에서만 유지된다. 보통 JPA의 영속성 컨텍스트는 트랜잭션의 생명주기를 따라가는데, OSIV

    brightmango.tistory.com

     

    fetch-join의 단점

    모든 기술에는 장,단점이 있듯이 fetch-join에도 단점과 한계가 존재한다.

    1. 둘 이상의 컬렉션에는 적용할 수 없다.
      1.  B와 C 모두에 대해 가져오기 조인을 적용하면 B와 C의 각 조합이 중복되는 결과 집합이 생성된다.
    2. 페이징을 적용할 수 없다.
      1. 데이터 수를 예측할 수 없다.

       이런 한계를 극복하기 위해 batch-fetch-size를 지정해줄 수 있다. 

    저작자표시 비영리 변경금지 (새창열림)
      '프로젝트 기록' 카테고리의 다른 글
      • 스프링 세션의 이해 - 등장 배경과 그 원리
      • 2차 캐시를 이용한 최적화 기록
      • [프로젝트 기록] Redis 메인/레프리카 서버 만들기
      • [프로젝트 기록] Redis와 직렬화/역직렬화
      베어_
      베어_
      Today I learned | 문제를 해결하는 개발자

      티스토리툴바