JPA休眠n + 1问题(懒惰和渴望差异) [英] JPA Hibernate n+1 issue (Lazy & Eager Diff)

查看:72
本文介绍了JPA休眠n + 1问题(懒惰和渴望差异)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图了解n + 1问题,从而找到适当的解决方法.

I am trying to understand n+1 problem and thus find the proper fix.

我有两个实体: 公司

@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;
    }

}

每个公司都有很多员工.如此简单的UNI-DIRECTIONAL一对多关系. 现在,当我运行查询(从公司a中选择a")时,我正面临着n + 1个选择(当我试图获取员工时)

Each company have many employee. So simple UNI-DIRECTIONAL One to Many Relationship. Now when i am running query ("Select a from Company a"), i am facing n+1 selects(when i am trying to get employee)

但是为了更清楚地理解这些概念,当我将其更改为EAGER时,所有相同的n + 1查询最初都在运行(即使我没有提取员工).这是正确的行为吗?我的意思是不应该触发联接查询.另外,如何使用EAGER更改代码以仅产生1个查询.

But to understand the concepts more clearly, when i am changing it to EAGER, all the same n+1 queries are running initially(even when I did not fetch the employee). Is it the right behaviour? I mean shouldn't it fire a join query instead. Also how do I change code with EAGER to result in only 1 query.?

推荐答案

问题"并不是真正的问题,它与ORM的工作方式有关. Hibernate创建所谓的嵌套查询(如果您访问这样的关联).

The "issue" is not really an issue, it's about how ORMs work. Hibernate creates so called nested queries if you access an association like that.

您是正确的,在两种情况下都执行相同的查询,将映射的FetchTypeLAZY切换到EAGER仅是安排执行其他(n + 1)个查询.

You are right, in both cases the same queries are executed, switching the FetchType of your mapping from LAZY to EAGER is only scheduling the execution of the additional (n+1) queries.

假设您有很多公司,所有公司都有雇员,在两种情况下都执行了这样的查询(至少一次):

Assume you have many companies, all of them have employees, in both cases queries like that are executed (at least once):

select ... from company company0_

select ... from employee employee0_ where employee0_.cmp_id=?

执行第一个命令以获取所有公司,第二个为每个公司执行一次.

The first one is executed to get all companies, the second one for every company once.

例如:3个拥有很多员工的公司(N)将执行一次第一次选择,而三个嵌套选择= 3 + 1 = 4个查询之和.

E.g.: 3 companies (N) with many employees will execute the first select once and three nested selects = 3+1 = 4 queries in sum.

EAGERLAZY之间的区别仅在于时间点,通常无法避免数据库访问,因为您仍然需要数据.使用LAZY,附加查询将被推迟,直到您迭代员工集合为止.但是请记住,这只是一个提示,并非每个数据库驱动程序都支持延迟加载.

The difference between the EAGER and LAZY is just about the point in time, you can't avoid database access in general because you nevertheless need the data. With LAZY the additional query is just postponed until you iterate the employee collection(s). But keep in mind that it's just a hint, not every database driver is supporting lazy loading.

如果您真的知道自己始终需要数据,可以编写一个FETCH JOIN查询并一次性接收所有需要的数据:

If you really know that you always need the data you can write a FETCH JOIN query and receive all needed data in one shot:

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

这将避免第二次数据库访问.为了验证您的测试,也许数据源代理项目为你.

That will avoid the second database access. To verify that in your tests, maybe the datasource proxy project is something for you.

这篇关于JPA休眠n + 1问题(懒惰和渴望差异)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆