Ank*_*sal 5 hibernate spring-data-jpa
我试图了解n + 1问题,从而找到适当的解决方法。
我有两个实体:公司
@Entity
@Table(name="company")
public class Company implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue
private int id;
@Column(name="cmp_id")
private int cmpId;
@Column(name="company_name")
private String companyName;
@OneToMany(fetch=FetchType.LAZY)
@JoinColumn(name="cmp_id",referencedColumnName="cmp_id")
private Set<Employee> employee;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getCmpId() {
return cmpId;
}
public void setCmpId(int cmpId) {
this.cmpId = cmpId;
}
public String getCompanyName() {
return companyName;
}
public void setCompanyName(String companyName) {
this.companyName = companyName;
}
public Set<Employee> getEmployee() {
return employee;
}
public void setEmployee(Set<Employee> employee) {
this.employee = employee;
}
}
Run Code Online (Sandbox Code Playgroud)
雇员
@Entity
@Table(name="employee")
public class Employee implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue
private int id;
@Column(name="emp_id")
private int empId;
@Column(name="emp_name")
private String empName;
/*@ManyToOne(fetch=FetchType.LAZY)
@JoinColumn(name="cmp_id", referencedColumnName="cmp_id")
@JsonIgnore
private Company company;*/
@Column(name="cmp_id")
private int cmpId;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getEmpId() {
return empId;
}
public void setEmpId(int empId) {
this.empId = empId;
}
public String getEmpName() {
return empName;
}
public void setEmpName(String empName) {
this.empName = empName;
}
}
Run Code Online (Sandbox Code Playgroud)
每个公司都有很多员工。如此简单的UNI-DIRECTIONAL一对多关系。现在,当我运行查询(“从公司a中选择a”)时,我正面临n + 1个选择(当我尝试获取员工时)
但是为了更清楚地理解这些概念,当我将其更改为EAGER时,所有相同的n + 1查询最初都在运行(即使我没有提取员工)。这是正确的行为吗?我的意思是不应该触发联接查询。另外,如何使用EAGER更改代码以仅产生1个查询。
“问题”并不是真正的问题,它与ORM的工作方式有关。如果您访问这样的关联,Hibernate会创建所谓的嵌套查询。
没错,在两种情况下都执行相同的查询FetchType,将映射从切换LAZY到EAGER只是安排其他(n + 1)个查询的执行。
假设您有很多公司,并且所有公司都有雇员,在两种情况下都将执行这样的查询(至少一次):
select ... from company company0_
select ... from employee employee0_ where employee0_.cmp_id=?
Run Code Online (Sandbox Code Playgroud)
第一个执行以获取所有公司,第二个执行每个公司一次。
例如:3个拥有很多员工的公司(N)将执行一次第一次选择,三个嵌套选择= 3 + 1 = 4个查询。
EAGER和之间的区别LAZY就在时间点上,您通常无法避免访问数据库,因为您仍然需要数据。随着LAZY额外的查询被推迟,直到您迭代雇员集合。但是请记住,这只是一个提示,并非每个数据库驱动程序都支持延迟加载。
如果您真的知道自己始终需要数据,则可以编写FETCH JOIN查询并一次性接收所有需要的数据:
Select c from Company c JOIN FETCH c.employee e
Run Code Online (Sandbox Code Playgroud)
这将执行如下查询:
select ... from company company0_ inner join employee employee1_ on company0_.cmp_id=employee1_.cmp_id
Run Code Online (Sandbox Code Playgroud)
这样可以避免第二次数据库访问。为了验证您的测试,也许数据源代理项目适合您。
要更深入地了解什么是,n+1请查看这个已经得到很好回答的问题。
为了解决您的问题,您可以通过在 jpa 存储库中编写此查询来急切地获取所有子实体Company。
@Query("Select c from Company c join fetch c.employee e where c.id = :cmpId")
public Company fetchCompanyAndEmployeesEager(@Param("cmpId") long id);
Run Code Online (Sandbox Code Playgroud)
PS:我还没有测试过该查询,但它应该可以工作。
| 归档时间: |
|
| 查看次数: |
4569 次 |
| 最近记录: |