2017-06-20 35 views
0

我需要幫助在JPA中生成查詢。比方說,我有這些表JPA:如何定義沒有N + 1問題的複雜計數

create table PERSON (
    person_id number(3,0) not null primary key, 
    last_name varchar2(15) not null, 
    first_name varchar2(15) not null, 
    department varchar2(15) not null 
); 

create table PHONE (
    phone_id number(3,0) not null primary key, 
    person_id number(3,0) not null, 
    phone_type varchar2(10) not null, 
    CONSTRAINT fk_person_id FOREIGN KEY (person_id) 
    REFERENCES person (person_id) 
); 

我想定義諸如查詢:定義如下

select p1.person_id, p1.last_name, p1.first_name, 
     (select count(1) from phone p2 
     where p2.person_id = p1.person_id) as phone_count 
from person p1; 

我的JPA類:

@Entity 
@Table(name="PERSON") 
@Transactional(readOnly=true) 
public class Person { 
    @Id 
    @Column(name = "PERSON_ID") 
    private Integer personId; 

    @Column(name = "LAST_NAME") 
    private String lastName; 

    @Column(name = "FIRST_NAME") 
    private String firstName; 

    @Column(name = "DEPARTMENT") 
    private String department; 

    @OneToMany 
    @LazyCollection(LazyCollectionOption.FALSE) 
    @Fetch(FetchMode.SELECT) // or @Fetch(FetchMode.JOIN) 
    @JoinColumn(name = "PERSON_ID", referencedColumnName="PERSON_ID") 
    private Set<Phone> phones; 

    .... getters and setters... 
} 


@Entity 
@Table(name="PHONE") 
@Transactional(readOnly=true) 
public class Person { 
    @Id 
    @Column(name = "PHONE_ID") 
    private Integer phoneId; 

    @Column(name = "PERSON_ID") 
    private Integer personId; 

    @Column(name = "PHONE_TYPE") 
    private String phoneType; 

    .... getters and setters ... 
} 

因爲我有動態where子句,即姓氏,名字,部門組合可以根據需要傳入。我有一個函數:

personRepository.findAll(spec, pageable); 

,幫助我解決where子句,然後我通過getPhones.size拿到手機數量的動態()。它按預期工作。但是,它會產生N + 1問題。如果我有500多人,它會循環每個人的記錄並在電話桌上呼叫選擇查詢。我的問題是如何在這種情況下避免N + 1問題。

我想使用本機查詢以及我不知道如何使動態where子句工作(我真正的程序有超過15列過濾)。幫助表示讚賞。

+0

通常對於普通的查詢,我會簡單地建議添加'來自Person p left join fetch p.phones'。雖然不熟悉'Specification'的使用。一個快速檢查似乎表明'Root'允許你定義'fetch',這又轉化爲一個聯合抓取,爲此你可以在構建規範時做些什麼 –

+0

@Fetch(FetchMode.JOIN)將執行左連接取,我假設,但沒有。它仍然給了我N + 1個查詢。 –

+0

我幾乎從不依賴實體中的渴望獲取定義,因爲1.它不總是工作,2.我無法控制。我寧願控制查詢中的取回行爲,而不是 –

回答

0

等效JPA查詢是:

SELECT 
    p.personId, p.lastName, p.firstName, 
    (SELECT COUNT(ph) FROM p.phones ph) 
WHERE ... 

這將返回元組,即使用它作爲:

TypedQuery<Object[]> q = em.createQuery(_QUERY_FROM_ABOVE_, Object[].class); 
List<Object[]> results = q.getResultList(); 
// results.get(0)[0] -> Integer personId 
// results.get(0)[1] -> String lastName 
// results.get(0)[2] -> String firstName 
// results.get(0)[3] -> Long COUNT 

你甚至可以要求整個Person,以其計數一起電話:

SELECT p, (SELECT COUNT(ph) FROM p.phones ph) 
WHERE ... 

With code類似於上面的那個,results.get(0)[0]是整個Person對象,results.get(0)[1]是計數爲Long

+0

或者「元組」 –

+0

謝謝你的迴應。此解決方案適用於靜態where子句。在我的場景中,where子句基於傳入的過濾器生成。 我通過使用計數創建視圖表找到了此問題的解決方案。從JPA,我可以在視圖表上運行普通查詢findAll(spec,pageable)。 –