java - JPQL,是否可以使我选择的对象的子集合不同?
问题描述
我有以下 JPQL 查询:
@Query("SELECT project FROM Project project JOIN FETCH project.employees employee JOIN FETCH project.order ord " +
"JOIN FETCH ord.customer JOIN FETCH project.defaultDailyEntrySettings LEFT JOIN FETCH employee.projectEmployeeRoles role " +
"LEFT JOIN FETCH role.project roleProject LEFT JOIN FETCH roleProject.defaultDailyEntrySettings " +
"WHERE project.id = :id")
Project test(@Param("id") long id);
问题是它返回内部重复的员工project.employees
。employee.projectEmployeeRoles
对于员工内部的每个角色,它在project.employees
列表中出现一次。那是由JOIN FETCH employee.projectEmployeeRoles role
. 所以如果员工有 7 个角色,它将在project.employees
列表中出现 7 次。有没有办法project.employees
区分,或者有没有其他方法可以确保员工只在列表中出现一次?我可以在 java 中删除重复项,但如果查询首先不返回任何重复项会更好。
如果我删除LEFT JOIN FETCH
结果是正确的并且不包含任何重复的员工。但是这里的问题是employee.projectEmployeeRoles
每个员工都懒惰地获取,这会导致太多的性能问题。
编辑:
我将 JPA 与休眠一起使用。以下是上述实体的连接定义:
项目:
@Data
public class Project {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
@ManyToMany
@JsonIgnore
@JoinTable(
name = "employee_projects",
joinColumns = @JoinColumn(name = "project_id"),
inverseJoinColumns = @JoinColumn(name = "employee_id")
)
private List<Employee> employees;
@OneToMany(mappedBy = "project", cascade = CascadeType.ALL, orphanRemoval = true)
private List<ProjectEmployeeRole> projectEmployeeRoles;
// more fields
}
员工:
@Data
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
@ManyToMany
@JsonIgnore
@JoinTable(
name = "employee_projects",
joinColumns = @JoinColumn(name = "employee_id"),
inverseJoinColumns = @JoinColumn(name = "project_id")
)
private List<Project> projects;
@ManyToMany
@JsonIgnore
@JoinTable(
name = "employee_roles",
joinColumns = @JoinColumn(name = "employee_id"),
inverseJoinColumns = @JoinColumn(name = "role_id")
)
private Set<ProjectEmployeeRole> projectEmployeeRoles;
// more fields
}
角色:
public class ProjectEmployeeRole {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
@ManyToOne
@JsonIgnore
@NotNull
private Project project;
@ManyToMany
@JsonIgnore
@JoinTable(
name = "employee_roles",
joinColumns = @JoinColumn(name = "role_id"),
inverseJoinColumns = @JoinColumn(name = "employee_id")
private List<Employee> employees;
// more fields
编辑2:
我现在一次加载一个集合,并且能够避免 n+1 请求和笛卡尔积。我也没有任何重复了:
服务:
Project project = projectRepository.findProjectInOneRequestById(id);
project.setEmployees(employeeRepository.findIneOneRequestByEmployees(project.getEmployees()));
查询:
@Query("SELECT project FROM Project project LEFT JOIN FETCH project.employees employee JOIN FETCH project.order ord " +
"JOIN FETCH ord.customer LEFT JOIN FETCH project.defaultDailyEntrySettings " +
"WHERE project.id = :id")
Project findProjectInOneRequestById(@Param("id") long id);
@Query("SELECT DISTINCT employee FROM Employee employee LEFT JOIN FETCH employee.projectEmployeeRoles role " +
"LEFT JOIN FETCH role.project roleProject WHERE employee IN :employees")
List<Employee> findIneOneRequestByEmployees(@Param("employees") List<Employee> employees);
解决方案
是的,当您不使用 JOIN FETCH 时,这是可能的。在您的情况下,Hibernate 似乎正在生成一个笛卡尔积作为您的查询结果。我注意到您正在使用 Set in private Set<ProjectEmployeeRole> projectEmployeeRoles;
。如果将其更改为 List,则可能会收到 MultipleBagFetchException。我猜你之前遇到过这个问题,并且正在尝试使用 Set 来解决它,但结果是现在你有了一个笛卡尔积。
我建议您在此处评估是否真的需要 JOIN FETCH。为了理解 Hibernate 的笛卡尔问题以及如何处理 MultipleBagFetchException 我建议这些文章:
推荐阅读
- javascript - 规范化后如何保留键索引对象的项目顺序(来自服务器)?
- css - 如何使用 React 组件避免 z-index 错误
- caching - 有没有办法在 NativeScript Angular 应用程序中禁用 WebView(iOS 和 Andriod)中的所有缓存?
- autodesk-forge - Autodesk forge viewer.js 从 6.6 更新到 7.1 findPropertyDbPath 返回 null
- reactjs - 在 React 中分离 axios api
- arrays - one-hot 编码问题
- python - 解决“从数据帧复制切片”的问题
- ansible - 使用ansible生成随机密码时如何选择强制字符?
- php - 我想通过 php 手动编写 url 来防止用户在我的网络中转移
- android - 为什么按钮会上升?