JPQL, возможно ли сделать дочернюю коллекцию объекта, который я выбираю, отличной? - PullRequest
0 голосов
/ 08 января 2020

У меня следующий запрос 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);

1 Ответ

1 голос
/ 08 января 2020

Да, это возможно, если вы не используете JOIN FETCH, как вы заметили. Похоже, что в вашем случае Hibernate генерирует декартово произведение как результат вашего запроса. Я заметил, что вы используете набор приватно Set<ProjectEmployeeRole> projectEmployeeRoles;. Если вы измените его на список, вероятно, вы получите исключение MultipleBagFetchException. Я предполагаю, что у вас была эта проблема раньше, и вы пытаетесь ее исправить с помощью набора, но в результате у вас есть декартово произведение.

Я бы посоветовал вам оценить, действительно ли вам нужна JOIN FETCH здесь. Чтобы решить декартову проблему с Hibernate, а также как справиться с MultipleBagFetchException, я бы предложил следующие статьи:

MultipleBagFetchException

декартово произведение - Hibernate

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...