(我为这个问题做了SSCCE。)
我有两个简单的实体:Employee
和Company
。 Employee
与@ManyToOne
的{{1}}关系具有默认提取策略(渴望)。
我希望能够在不更改Company
中定义的获取策略的情况下加载Employee
而不更改Company
,因为我只需要针对一个用例执行此操作。
JPA的实体图似乎是为了这个目的。
所以我在课程Employee
上定义了@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
:
EmployeeRepository
尽管使用了public interface EmployeeRepository extends CrudRepository<Employee, Integer> {
@EntityGraph(value = "employeeOnly", type = EntityGraph.EntityGraphType.FETCH)
List<Employee> findByCompanyId(Integer companyId);
}
,但我可以在日志中看到@EntityGraph
仍由hibernate加载:
Company
为什么呢?如何避免?
答案 0 :(得分:8)
目前,即使使用实体图,Hibernate也不支持将非惰性属性视为惰性。对此有一个未解决的问题:HHH-8776。
因此,目前唯一的解决方案是使关联变得懒惰。
答案 1 :(得分:2)
per - specification,@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);
在测试中,这是由loadByCompanyId()
生成的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=?
这是由使用@Query
注释的方法生成的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_ where employee0_.company_id=?
您可以查看my repository中的最新代码。
HTH。
答案 2 :(得分:0)
我的印象是您必须在图表定义中指定字段。
https://docs.oracle.com/javaee/7/tutorial/persistence-entitygraphs001.htm(43.1.2.1获取图表)
获取图仅包含在中明确指定的字段 EntityGraph实例,并忽略默认的实体图设置。
答案 3 :(得分:0)
似乎是Hibernate中的一个错误。
@Dragan Bozanovic你是对的。在这种情况下,我只看到一种解决方法。
设置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=?
答案 4 :(得分:0)
也许不那么酷的解决方案是使用原生查询。
@Query(value = "select * from employee where company_id= ?1", nativeQuery = true)
List<Employee> findByCompanyId(Integer companyId);
我测试了它并且它给出了预期的结果,而不是被迫设置fetch = FetchType.LAZY
答案 5 :(得分:0)
虽然通过指定 (fetch=FetchType.Lazy) 关联是惰性的,但公司通过单独的查询调用公开,因为您公开了具有财产公司的 Entity 员工。如果您不希望公司被曝光,那么要么使用@JsonIgnore,这不是非常推荐的。这是我们使用 DTO 以便我们可以只公开必需字段的原因之一。您可以创建具有员工特定字段的 EmployeeDTO 保持关联延迟,这将确保不会执行单独的查询来获取公司详细信息并将实体显式映射到 DTO,或者您可以使用 MapSturct Api