복붙노트

[SPRING] FetchMode 조인은 Spring JPA 저장소의 ManyToMany 관계에 아무런 차이가 없습니다.

SPRING

FetchMode 조인은 Spring JPA 저장소의 ManyToMany 관계에 아무런 차이가 없습니다.

나는 이것을하려고 노력하고있다 :

//...
class Person {
    @ManyToMany(fetch = FetchType.EAGER)
    @Fetch(FetchMode.JOIN)
    private Set<Group> groups;
//...
}

personRepository.findAll ();을 수행 할 때 n + 1 개의 쿼리를 생성합니다. Spring JPA 저장소를 통해 마치 @Fetch 세트가없는 것처럼 말입니다. (먼저 모든 사람을 얻으려면 하나의 쿼리를, 그 다음 그룹을 가져 오려면 한 사람당 하나의 쿼리).

그래도 @Fetch (FetchMode.SUBSELECT)를 사용하면 효과가 있습니다! 2 개의 쿼리 만 생성합니다. (모든 사람에게 한 번, 그리고 그룹에 한 번). 따라서 최대 절전 모드는 JOIN이 아닌 일부 가져 오기 매개 변수에 반응합니다.

나는 또한 운이없는 EAGER 페칭 제거를 시도했다.

//...
class Person {
    @ManyToMany()
    @Fetch(FetchMode.JOIN)
    private Set<Group> groups;
//...
}

나는 Spring JPA를 사용하고 있는데 이것은 나의 저장소를위한 코드이다 :

public interface PersonRepository extends JpaRepository<Person, Long> {
}

JOIN은 Spring JPA를 통해서만 작동하지 않습니까? 아니면 제가 잘못한 것을하고 있습니까?

해결법

  1. ==============================

    1.많은 포럼과 블로그를 통해 문제를 읽을 수 있습니다. (내가 여기에 게시하기 전에 그렇게했을 수도 있습니다.) 나도 그렇게 생각합니다.

    많은 포럼과 블로그를 통해 문제를 읽을 수 있습니다. (내가 여기에 게시하기 전에 그렇게했을 수도 있습니다.) 나도 그렇게 생각합니다.

    참고 문헌 최대 절전 모드 포럼 춘계 포럼 비슷한 질문 1

  2. ==============================

    2.JPA를 사용할 때 @Fetch (FetchMode.JOIN)을 사용할 수 없었습니다 (최대 절전 기준 API를 사용할 때 제대로 작동하지만). 이유를 설명하는 예제도 찾을 수 없었지만 몇 가지 해결 방법을 생각할 수 있습니다. .

    JPA를 사용할 때 @Fetch (FetchMode.JOIN)을 사용할 수 없었습니다 (최대 절전 기준 API를 사용할 때 제대로 작동하지만). 이유를 설명하는 예제도 찾을 수 없었지만 몇 가지 해결 방법을 생각할 수 있습니다. .

    그룹을 열심히로드하는 가장 간단한 방법은 JPQL을 사용하는 것입니다.

    public interface PersonRepository extends JpaRepository<Person, String>{
      @Query(value = "select distinct p from Person p left join fetch p.groups")
      List<Person> getAllPersons();
    }
    

    spring-data-jpa를 사용함에 따라, Specification을 사용하여 Group을 열심히로드 할 수도 있습니다. (1.4.x에서는 null을 리턴하는 스펙을 연결할 수 있습니다).

    final Specification<Person> fetchGroups = new Specification<Person>() {
        @Override
        public Predicate toPredicate(Root<Person> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
            root.fetch("groups", JoinType.LEFT);
            query.distinct(true);
            return null;
        }
    };
    

    이들 중 어느 것도 옵션이 아니라면 @Fetch (FetchMode.SUBSELECT)를 사용하는 것이 가장 좋습니다.

    또 다른 옵션은 @BatchSize와 함께 @Fetch (FetchMode.SELECT)를 사용하는 것입니다. @BatchSize는 n + 1 쿼리의 문제를 해결하는 데 도움이됩니다. 배치 크기를 조정하면 CEIL (n / batch_size) +1로 실행되는 쿼리의 양을 줄일 수 있습니다.

    @Entity
    @Table(name = "persons")
    public class Person {
      @Id
      String name;
    
      @ManyToMany(fetch = FetchType.EAGER)
      @BatchSize(size = 20)
      Set<Group> groups = new HashSet<>();
    }
    
    @Entity
    @Table(name = "groups")
    public class Group {
      @Id
      String name;
    
      @ManyToMany(mappedBy = "groups", fetch = FetchType.LAZY)
      Set<Person> persons = new HashSet<>();
    }
    
    public interface PersonRepository extends JpaRepository<Person, String>{}
    

    personRepository.findAll ()을 실행할 때이 매핑 결과는 다음과 같습니다. 10 명을 포함하고 @BatchSize가 5로 설정된 데이터베이스에서.

    Hibernate: 
    select
        person0_.name as name1_ 
    from
        persons person0_
    Hibernate: 
    select
        groups0_.persons_name as persons1_1_1_,
        groups0_.groups_name as groups2_1_,
        group1_.name as name0_0_ 
    from
        persons_groups groups0_ 
    inner join
        groups group1_ 
            on groups0_.groups_name=group1_.name 
    where
        groups0_.persons_name in (
            ?, ?, ?, ?, ?
        )
    Hibernate: 
    select
        groups0_.persons_name as persons1_1_1_,
        groups0_.groups_name as groups2_1_,
        group1_.name as name0_0_ 
    from
        persons_groups groups0_ 
    inner join
        groups group1_ 
            on groups0_.groups_name=group1_.name 
    where
        groups0_.persons_name in (
            ?, ?, ?, ?, ?
        )
    

    @BatchSize는 FetchType.LAZY로 매핑 된 콜렉션에서도 작동합니다.

  3. from https://stackoverflow.com/questions/18891789/fetchmode-join-makes-no-difference-for-manytomany-relations-in-spring-jpa-reposi by cc-by-sa and MIT license