Понимание того, как spring -data обрабатывает @EntityGraph

(Я сделал SSCCE для этого вопроса.)

У меня есть 2 простых объекта: Employee и Company. Employee имеет отношение @ManyToOne с Company с стратегией выборки по умолчанию (нетерпеливо).

Я хочу иметь возможность загружать Employee без Company без изменения стратегии выборки, определенной в Employee, потому что мне нужно сделать это только для одного случая использования.

График сущности JPA, по-видимому, предназначен для этой цели.

Итак, я определил a @NamedEntityGraph для класса Employee:

@Entity
@NamedEntityGraph(name = "employeeOnly")
public class Employee {

  @Id
  private Integer id;
  private String name;
  private String surname;
  @ManyToOne
  private Company company;

  //Getters & Setters

И a EmployeeRepository следующим образом:

public interface EmployeeRepository extends CrudRepository<Employee, Integer> {

  @EntityGraph(value = "employeeOnly", type = EntityGraph.EntityGraphType.FETCH)
  List<Employee> findByCompanyId(Integer companyId);

}

Несмотря на использование @EntityGraph, в журналах я вижу, что Company по-прежнему загружается спящим:

2016-11-07 23:16:08.738 DEBUG 1029 --- [nio-8080-exec-2] org.hibernate.SQL                        : select employee0_.id as id1_1_, employee0_.company_id as company_4_1_, employee0_.name as name2_1_, employee0_.surname as surname3_1_ from employee employee0_ left outer join company company1_ on employee0_.company_id=company1_.id where company1_.id=?
2016-11-07 23:16:08.744 DEBUG 1029 --- [nio-8080-exec-2] org.hibernate.SQL                        : select company0_.id as id1_0_0_, company0_.name as name2_0_0_ from company company0_ where company0_.id=?

Почему? Как этого избежать?

Ответ 1

В настоящее время Hibernate не поддерживает обработку нелазных атрибутов как ленивых, даже с графами сущностей. Для этого есть открытая проблема: HHH-8776.

Таким образом, единственное решение на данный момент состоит в том, чтобы сделать ассоциацию ленивой.

Ответ 2

Измененный ответ

per- спецификация, тип выборки для @ManyToOne по умолчанию равен EAGER. Но даже через мы установили:

@ManyToOne(fetch = FetchType.LAZY)
private Company company;

Вы получите тот же результат. Проблема в том, что способ spring -data-jpa создает для вас HQL/JPQL. Поэтому добавить @ManyToOne(fetch = FetchType.LAZY) не будет. недостаточно. Чтобы решить эту проблему, используйте @ManyToOne(fetch = FetchType.LAZY) и @Query аннотацию в вашем репозитории:

Employee.java:

@ManyToOne(fetch = FetchType.LAZY)
private Company company;

EmployeeRepository.java

@Query("from Employee e where e.company.id = :companyId")
List<Employee> findByCompanyIdUsingQuery(@Param("companyId") Integer companyId);

В тесте это SQL, сгенерированный вашим loadByCompanyId() (который генерирует левое внешнее соединение):

select employee0_.id as id1_1_, employee0_.company_id as company_4_1_, employee0_.name as name2_1_, employee0_.surname as surname3_1_ from employee employee0_ left outer join company company1_ on employee0_.company_id=company1_.id where company1_.id=?

И это SQL, сгенерированный методом, который использует аннотацию @Query:

select employee0_.id as id1_1_, employee0_.company_id as company_4_1_, employee0_.name as name2_1_, employee0_.surname as surname3_1_ from employee employee0_ where employee0_.company_id=?

Вы можете проверить последний код в мой репозиторий.

НТН.

Ответ 3

У меня было впечатление, что вы должны указывать поля в определении графика.

https://docs.oracle.com/javaee/7/tutorial/persistence-entitygraphs001.htm (43.1.2.1 Fetch Graphs)

Граф выборки состоит только из полей, явно указанных в EntityGraph и игнорирует настройки графа объектов по умолчанию.

Ответ 4

Кажется, ошибка в спящем режиме.

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

Установить fetch = lazy

@Entity
@NamedEntityGraph(name = "Employee.withCompany" , attributeNodes = @NamedAttributeNode("company"))
public class Employee {

  @Id
  private Integer id;
  private String name;
  private String surname;
  @ManyToOne(fetch = FetchType.LAZY)
  private Company company;

Внедрите новый метод для загрузки компании с нетерпением

public interface EmployeeRepository extends CrudRepository<Employee, Integer> {


  List<Employee> findByCompanyId(Integer companyId);

  @Query("select e from Employee e left join e.company c where c.id = :companyId")
  @EntityGraph(value = "Employee.withCompany", type = EntityGraph.EntityGraphType.FETCH)
  List<Employee> findByCompanyIdFetchingCompany(@Param("companyId") Integer companyId);

}

И используйте следующие два взаимозаменяемых, если требуется

  @RequestMapping(value = "/by-company/{id}")
  public void loadByCompanyId(@PathVariable Integer id) {
    employeeService.loadByCompanyId(id);
  }

  @RequestMapping(value = "/by-company/eager-company/{id}")
  public void loadByCompanyIdFetchingCompany(@PathVariable Integer id) {
    employeeService.loadByCompanyIdFetchingCompany(id);
  }

Первый (для ленивой загрузки) http://localhost:8080/employees/by-company/42

select employee0_.id as id1_1_, employee0_.company_id as company_4_1_, employee0_.name as name2_1_, employee0_.surname as surname3_1_ from employee employee0_ left outer join company company1_ on employee0_.company_id=company1_.id where company1_.id=?

Вторая (загруженная загрузка) http://localhost:8080/employees/by-company/eager-company/42

select employee0_.id as id1_1_0_, company1_.id as id1_0_1_, employee0_.company_id as company_4_1_0_, employee0_.name as name2_1_0_, employee0_.surname as surname3_1_0_, company1_.name as name2_0_1_ from employee employee0_ left outer join company company1_ on employee0_.company_id=company1_.id where company1_.id=?

Ответ 5

Возможно, не так классное решение использует собственный запрос.

 @Query(value = "select * from employee where company_id= ?1", nativeQuery = true)
  List<Employee> findByCompanyId(Integer companyId);

Я тестировал его, и он давал ожидаемые результаты, не будучи вынужденным устанавливать fetch = FetchType.LAZY