Можно ли в этом случае избежать N + 1 запросов? - PullRequest
0 голосов
/ 07 января 2020

У меня есть следующая модель класса:

enter image description here

Я реализовал это следующим образом:

Проект:

@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<Role> roles;

  // 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 List<Role> roles;

  // more fields
}

Роль:

public class Role {

  @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
  )
}

Таким образом, каждый проект имеет несколько сотрудников и ролей. В моем приложении я могу добавить сотрудника с ролью в проект. Так что они всегда пара. Сотрудник в проекте всегда играет одну роль. Я контролирую это в businesslogi c.

Вопрос: Учитывая проект, возможно ли найти всех его сотрудников, связать каждого сотрудника с его ролью в этом проекте (вот роль, которая связана как с сотрудником, так и с данным проектом, я удостоверяюсь, что всегда есть только одна роль, которая выполняет это условие) в одном запросе?

Так что вызов findEmployeeWithRole(project) должен вернуть список этого класса который содержит всех сотрудников проекта с их связанной ролью для этого проекта:

public class EmployeeAndRole {

  private Employee employee;
  private Role role;
}

В настоящее время я могу достичь этого только в n + 1 запросах:

Один запрос, чтобы найти всех сотрудников проекта:

@Query("SELECT project.employees FROM Project project WHERE project.id = :id")
List<Employee> findByProjectId(@Param("id") long id)

И затем n запросов (для каждого сотрудника в списке), чтобы найти роль, с которой сотрудник связан в этом проекте:

@Query("SELECT role FROM Role role WHERE role.project.id = :projectId AND :employeeId IN (SELECT employees.id FROM role.employees employees)
Role findByProjectIdAndEmployeeId(@Param("projectId") long projectId, @Param("employeeId") long employeeId)

Могу ли я достичь это в одном запросе? Если нет, каковы другие мои варианты, я должен попытаться переделать мою диаграмму класса?

Ответы [ 2 ]

0 голосов
/ 07 января 2020

Да, это можно сделать одним запросом. Ваш запрос будет выглядеть примерно так -

@Query(value = "select employee, role " +
            "from Employee as employee " +
            "inner join Project as project on employee member of project.employees " +
            "inner join Role as role on project = role.project and employee member of role.employees " +
            "where " +
            "  project.id = :projectId")
List<Tuple> getEmployeeRoles(Long projectId);

Вы можете вернуть более симпатичный список List<EmployeeAndRole>, используя конструктор clas в запросе jpa.

Cheers!

0 голосов
/ 07 января 2020

Я не знаю, правильно ли я понимаю, и если вы ищете более гибернационное решение или SQL решение, но я думаю, что это сработает:

SELECT
    p.employees

FROM project p

INNER JOIN employee e
    ON e.project.id = p.id      -- this ensure to get all emps from the selected project

LEFT JOIN role r                -- left join ensure to keep all records from employee, even if he hasn't a role
    ON  r.project.id = p.id     -- this ensure to get all role from the selected project
    AND r.employee.id = e.id    -- and linked to a employee too

WHERE
    p.id = :id

Это основано на вашем "вопросе "предложение.

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