Понимание того, как 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