提问人:Joe 提问时间:11/14/2023 最后编辑:Joe 更新时间:11/15/2023 访问量:34
Spring Boot 规格 root 的 fetch 函数用于连接列对性能的影响
Spring Boot Specifications root's fetch function for join columns effects on performance
问:
假设我有一个结构如下图所示的 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());
}
}
答:
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 FETCH
campus
getEmployeeList()
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,好吧。非常感谢您的澄清!
评论