2017-04-20 73 views
0

我想了解n + 1問題,從而找到適當的修復方法。JPA Hibernate n + 1問題(Lazy&Eager Diff)

我有兩個實體: 公司

@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個查詢。

回答

0

欲瞭解更多關於n+1退房的信息this已經很好的回答了問題。

要解決您的問題,您可以通過在Company jpa存儲庫中編寫此查詢來熱切獲取所有子實體。

@Query("Select c from Company c join fetch c.employee e where c.id = :cmpId") 
public Company fetchCompanyAndEmployeesEager(@Param("cmpId") long id); 

PS:我沒有測試過的查詢,但它應該工作。

1

「問題」並不是真正的問題,而是關於ORM如何工作的問題。如果你訪問這樣的關聯,Hibernate創建所謂的nested queries

你是對的,在這兩種情況下,執行相同的查詢,將映射的FetchTypeLAZY切換到EAGER只是調度附加(n + 1)查詢的執行。

假設你有很多公司,他們都有員工,在這兩種情況下,這樣的查詢執行(至少一次):

select ... from company company0_ 

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

第一個被執行,以獲取所有的公司,第二個爲每個公司一次。

例如:3家擁有衆多員工的公司(N)將執行第一次選擇一次和三次嵌套選擇= 3 + 1 = 4次查詢。

EAGERLAZY之間的差異恰好就是這個時間點,因爲您仍然需要數據,所以無法避免數據庫訪問。在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是適合你的東西。

+0

使用聯合抓取,抓取類型應該是懶惰還是渴望?和什麼是不同的B/W懶惰和渴望與聯合Fetch? –

+1

使用JOIN FETCH可以覆蓋每個渴望的映射策略。那麼映射是什麼並不重要。我會建議在映射中使用LAZY。 –