我试图理解 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;
}
}
员工
@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;
}
}
每个公司都有很多员工。如此简单的单向一对多关系。
现在,当我运行查询(“从公司 a 中选择一个”)时,我面临 n+1 个选择(当我试图获得员工时)
但是为了更清楚地理解这些概念,当我将其更改为 EAGER 时,所有相同的 n+1 查询最初都在运行(即使我没有获取员工)。这是正确的行为吗?我的意思是它不应该触发连接查询。另外,如何使用 EAGER 更改代码以仅产生 1 个查询。?
最佳答案
“问题”并不是真正的问题,而是关于 ORM 的工作方式。 Hibernate 创建了所谓的 nested queries如果您访问这样的协会。
你是对的,在这两种情况下都执行相同的查询,切换 FetchType
您的映射来自 LAZY
至 EAGER
仅调度附加 (n+1) 查询的执行。
假设你有很多公司,他们都有员工,在这两种情况下,这样的查询都会被执行(至少一次):
select ... from company company0_
select ... from employee employee0_ where employee0_.cmp_id=?
执行第一个以获取所有公司,第二个执行每个公司一次。
例如:3 家公司 (N) 和许多员工将执行第一次选择一次,三个嵌套选择 = 3+1 = 4 次查询。
EAGER
的区别和 LAZY
就在时间点上,您通常无法避免数据库访问,因为您仍然需要数据。与 LAZY
附加查询只是推迟到您迭代员工集合。但请记住,这只是一个提示,并非每个数据库驱动程序都支持延迟加载。如果你真的知道你总是需要数据,你可以写一个
FETCH JOIN
一键查询和接收所有需要的数据:Select c from Company c JOIN FETCH c.employee e
这将执行如下查询:
select ... from company company0_ inner join employee employee1_ on company0_.cmp_id=employee1_.cmp_id
这将避免第二次数据库访问。为了在您的测试中验证这一点,也许 the datasource proxy project是给你的东西。
关于hibernate - JPA Hibernate n+1 问题(Lazy & Eager Diff),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43513449/