У меня следующий запрос 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 ролей, он появится 7 раз в списке project.employees
. Есть ли способ сделать 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
Edit2:
Теперь я загружаю одну коллекцию за раз и могу избежать как 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);