JPQL,是否可以在选择不同时创建对象的子集合?
JPQL, is it possible to make a children collection of the object im selecting distinct?
我有以下 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);
问题是 return 在 project.employees
中有重复的员工。对于员工在 employee.projectEmployeeRoles
中的每个角色,它都会在 project.employees
列表中出现一次。那是JOIN FETCH employee.projectEmployeeRoles role
造成的。因此,如果员工有 7 个角色,它将在 project.employees
列表中出现 7 次。有没有办法让 project.employees
与众不同,或者有任何其他方法可以确保员工只在列表中出现一次?我可以删除 java 中的重复项,但如果查询一开始就没有 return 任何重复项会更好。
如果我删除 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 Set<ProjectEmployeeRole> projectEmployeeRoles;
。如果将其更改为列表,可能会出现 MultipleBagFetchException。
我猜你以前遇到过这个问题,并试图使用 Set 来修复它,但结果是现在你有一个笛卡尔积。
我建议您在此处评估是否真的需要 JOIN FETCH。为了理解 Hibernate 的笛卡尔问题以及如何处理 MultipleBagFetchException 我建议阅读这些文章:
我有以下 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);
问题是 return 在 project.employees
中有重复的员工。对于员工在 employee.projectEmployeeRoles
中的每个角色,它都会在 project.employees
列表中出现一次。那是JOIN FETCH employee.projectEmployeeRoles role
造成的。因此,如果员工有 7 个角色,它将在 project.employees
列表中出现 7 次。有没有办法让 project.employees
与众不同,或者有任何其他方法可以确保员工只在列表中出现一次?我可以删除 java 中的重复项,但如果查询一开始就没有 return 任何重复项会更好。
如果我删除 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 Set<ProjectEmployeeRole> projectEmployeeRoles;
。如果将其更改为列表,可能会出现 MultipleBagFetchException。
我猜你以前遇到过这个问题,并试图使用 Set 来修复它,但结果是现在你有一个笛卡尔积。
我建议您在此处评估是否真的需要 JOIN FETCH。为了理解 Hibernate 的笛卡尔问题以及如何处理 MultipleBagFetchException 我建议阅读这些文章: