Spring Boot 规格 root 的 fetch 函数用于连接列对性能的影响

Spring Boot Specifications root's fetch function for join columns effects on performance

提问人:Joe 提问时间:11/14/2023 最后编辑:Joe 更新时间:11/15/2023 访问量:34

问:

假设我有一个结构如下图所示的 Spring Boot 项目,我在其中存储 Campus 详细信息,其中有很多员工。此外,许多员工也只是一个部门的一部分,即部门是主数据实体。

public class Campus {
    @Id
    private String campusNo;

    @JsonManagedReference
    @OneToMany(cascade = CascadeType.ALL, mappedBy = "employee")
    private List<Employee>employeeList = new ArrayList<>();
}

public class Employee {
    @Id
    private String employeeCode;

    @JsonBackReference
    @ManyToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "campusNo", nullable = false)
    private Campus campus;

    @ManyToOne(cascade = CascadeType.ALL)
    @JoinColumn(referencedColumnName = "departMentNo")
    private Department department;
}

public class Department {
    @Id
    private String departMentNo;
}

现在,如果我想在我的项目中使用规范来创建可配置的报告,如下所示,以下两个“fetch()”语句(标记为选项 1选项 2)中的哪一个对性能有帮助(减少“select”语句的数量),为什么?他们俩都工作。我只对了解每个陈述的优点和缺点(如果有的话)感兴趣。

public class CampusSpecification implements Specification<Campus> {
    private SearchCriteria searchCriteria;

    @Override
    public Predicate toPredicate(Root<Campus> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
        root.fetch("employeeList").fetch("department"); // Option 1
        root.fetch("employeeList"); // Option 2
        return criteriaBuilder.in(root.get("employeeList").get("department").get("departMentNo")).value(searchCriteria.getValue());
    }
}
java spring-boot hibernate spring-data-jpa criteria-api

评论


答:

1赞 Sebastiaan van den Broek 11/15/2023 #1

一般而言,我不会过早地担心性能,除非您注意到请求花费了很长时间。我个人在使用 Spring Boot/Hibernate 时不使用 CriteriaBuilder,因为与仅使用 JPQL 或 SQL 相比,语句变得非常冗长,但最终对性能真正重要的是由此产生的查询。对于您的 2 个选项,这些很可能是相同的。

如果要检查这一点,可以监视数据库上的查询,或者将其放在:application.properties

spring.jpa.show-sql=true

所以你可以看到生成了什么样的 SQL。

请注意,Hibernate 本身的一个一般提示是,当你只想将它们导出为视图时,不要使用实体。

对于像这样具有一个 ManyToOne 联接和一个 OneToMany 联接的相当简单的查询,我认为整体性能不会那么糟糕。一旦你开始加入多个关联,这时你可能应该开始拆分主表查询和关联的数据表查询中的查询。

评论

0赞 Joe 11/15/2023
嗨,塞巴斯蒂安,我的项目中确实有多个这样的关联。上面的例子是其中一种情况的细分。我使用您的建议来显示正在生成的 SQL 类型。我看到无论是否使用“fetch”功能,“select”语句的数量基本上相同。我只想彻底,因为我的数据库现在很小,将来会继续增长。这就是为什么我想探索“fetch”功能,但文档不是很清楚。
0赞 Sebastiaan van den Broek 11/15/2023
fetch将转换为 JPQL 中的查询,这意味着它将是一个 SQL 联接,但来自联接实体的结果列也已加载到内存中。因此,一旦你有了你的对象并调用了 ,它实际上不会立即执行另一个查询,而只是从内存中获取数据。JOIN FETCHcampusgetEmployeeList()
0赞 Sebastiaan van den Broek 11/15/2023
因此,在不知道 CriteriaBuilder 在内部如何真正工作的情况下,但看到选项 2 没有执行第二个连接的获取,我会想象选项 1 总共执行的查询更少(一旦您实际执行类似getEmployeeList().get(0).getDepartment()
0赞 Joe 11/15/2023
@Sebastian,因此,实际上,使用“fetch”的唯一缺点是增加了我的应用程序服务器上的内存消耗,因为数据已被“预取”并存储在内存中。因此,它本质上是在向数据库触发的 SQL 查询数量与应用程序服务器中的内存消耗之间进行权衡。这意味着,如果我的数据库很大,我的应用程序服务器应该有足够的内存来支持相同的数据库。
1赞 Joe 11/15/2023
@Sebastian,好吧。非常感谢您的澄清!