尽管使用@Fetch(FetchMode.JOIN),但JpaRepository方法的“N+1选择问题”

问题描述 投票:0回答:1

尽管在一个非常复杂的软件中使用@Fetch(FetchMode.JOIN),我还是遇到了“N+1 选择问题”,为了简单起见,我创建了一个在 GitHub 上发布的示例。问题是我无法使用JOIN模式来获取数据,该行为总是产生“N+1选择问题”。我的主表中有一个非常大的数据集,在我的例子中,N 约为 10/15k,执行 JOIN 是强制性的。

这是代码(在 GitHub 上完整且可执行)

public class Student {
    @Id
    private String id;
    private String firstName;
    private String lastName;
//    @ManyToOne
//    @JoinColumn(name = "university_id")
//    private University university;
}

public class University {
    @Id
    private String id;
    private String name;
    private String address;
    @ToString.Exclude
    @OneToMany/*(fetch = FetchType.EAGER)*/
    @Fetch(FetchMode.JOIN)
    private Set<Student> students = new HashSet<>();
}

public interface StudentRepository extends JpaRepository<Student, String> {
}

public interface UniversityRepository extends JpaRepository<University, String> {
}

@RunWith(SpringRunner.class)
@SpringBootTest(classes = SpringBootTestClass.class)
@DataJpaTest
@Log4j2
public class FetchTypeTest {
    @Autowired
    private UniversityRepository repository;

    @Test
    public void findAll() {
        List<University> universityList = repository.findAll(new PageRequest(0, 100)).getContent();
        for (University university : universityList) {
            log.info("{} [{}]", university.getName(), university.getAddress());
            for (Student student : university.getStudents()) {
                log.info("{}, {}", student.getLastName(), student.getFirstName());
            }
        }
    }
}

期望看到 1 个且只有 1 个查询(左外)连接大学和学生,但执行测试日志显示 3 选择:大学 1,学生 2(有 2 所大学,每所大学 5 名学生) FetchMode.JOIN 用法。我尝试了 FetchMode / FetchType 的所有可行组合,但没有任何效果

依赖关系:

  • Java 8
  • 弹簧启动:1.5.8
  • spring-boot-starter-data-jpa:1.5.8
  • spring-data-jpa:1.11.18
  • 休眠核心:5.0.12
  • 休眠实体管理器:5.0.12
spring-boot hibernate jpa java-8 spring-data-jpa
1个回答
0
投票

FindAll*
spring-data-jpa
的方法实现了忽略获取模式的查询。当您编写查询时,您是在告诉什么已连接,什么未连接。

仅当使用

findById
等方法加载实体时或浏览其他实体图并加载其关联时,才会考虑获取模式。

你可以定义这样的方法

  @Query(value = "SELECT u FROM University u LEFT JOIN FETCH u.students s order by u.name")
  Page<Region> findAllUniversitiesWithPagination(Pageable pageable);

但是注意这个方法是在内存中分页的

HHH90003004: firstResult/maxResults specified with collection fetch; applying in memory

按照这篇post来解决这个问题。

© www.soinside.com 2019 - 2024. All rights reserved.