2012-09-17 35 views
0

EDIT(完全重新途徑):JPA + Hibernate的 - 內父母和子女表之間加入

我試圖推動新的項目使用JPA的,但我有一個所謂平凡的奮鬥問題:兩個表(父和子)之間的INNER JOIN。

我將只提供必要的信息,並將所有其他信息留下。請隨時詢問更多信息是否需要。有兩個表LANGUAGE和MESSAGE_RESOURCE,其中父表是LANGUAGE(主鍵ID_LANGUAGE),而子表具有也稱爲ID_LANGUAGE的父表的外鍵。

語言(父)類:

@Entity 
@Table(name = "PF_LANGUAGE") 
public class Language { 
    @Id 
    @Column(name = "ID_LANGUAGE", nullable = false) 
    @GeneratedValue(strategy = GenerationType.AUTO) 
    private int idLanguage; 

    @OneToMany(mappedBy="language",targetEntity=MessageResource.class, fetch=FetchType.EAGER) 
    private Collection<MessageResource> messageResources; 
} 

的子類:

@Entity 
@Table(name = "PF_MESSAGE_RESOURCE") 
public class MessageResource { 

    @Id 
    @Column(name = "ID_MESSAGE_RESOURCE", nullable = false) 
    @GeneratedValue(strategy = GenerationType.AUTO) 
    private int idMessageResource; 

    @ManyToOne(optional=false) 
    @JoinColumn(name="ID_LANGUAGE") 
    private Language language; 
} 

我取帶有命名查詢結果:

entityManager.createNamedQuery("select l, r from Language l join l.messageResources r"); 

這導致結果Object數組,其中每個條目包含一個語言MessageResource對。問題是,這是在單獨的查詢中完成的。

我可以在調試輸出中看到第一個查詢是兩個表之間的INNER JOIN,包含輸出中兩個表的列,所以這應該足夠了。 但是,JPA正在執行2次額外的查詢(LANGUAGE記錄的數量),以再次獲取每個父表的子表值,這不應該是必需的。

首先查詢這是足以讓所有的數據:

select 
    language0_.ID_LANGUAGE as ID1_5_0_, 
    messageres1_.ID_MESSAGE_RESOURCE as ID1_4_1_, 
    language0_.CODE as CODE5_0_, 
    language0_.DATE_INS as DATE3_5_0_, 
    language0_.DESCRIPTION as DESCRIPT4_5_0_, 
    messageres1_.DATE_INS as DATE2_4_1_, 
    messageres1_.KEY as KEY4_1_, 
    messageres1_.ID_LANGUAGE as ID5_4_1_, 
    messageres1_.VALUE as VALUE4_1_ 
from 
    PF_LANGUAGE language0_ 
inner join 
    PF_MESSAGE_RESOURCE messageres1_ 
on language0_.ID_LANGUAGE=messageres1_.ID_LANGUAGE 

兩個冗餘查詢,如下面也對之後的第一個內連接(他們是爲每種語言表中的記錄運行一次)數據庫運行:

select 
    messageres0_.ID_LANGUAGE as ID5_5_1_, 
    messageres0_.ID_MESSAGE_RESOURCE as ID1_1_, 
    messageres0_.ID_MESSAGE_RESOURCE as ID1_4_0_, 
    messageres0_.DATE_INS as DATE2_4_0_, 
    messageres0_.KEY as KEY4_0_, 
    messageres0_.ID_LANGUAGE as ID5_4_0_, 
    messageres0_.VALUE as VALUE4_0_ 
from 
    PF_MESSAGE_RESOURCE messageres0_ 
where 
    messageres0_.ID_LANGUAGE=? 

我需要消除由JPA生成的兩個冗餘附加查詢。第一個內部連接足以獲取所有數據。

需要此幫助。任何線索?

+0

什麼問題?你想要什麼樣的行爲? – axtavt

+0

我更新了問題信息,期望的行爲是INNER JOIN,但我雖然隱含了問題標題和文本流程 – JavaJuggler

+1

您是否嘗試更改fetch = FetchType.EAGER以使messageResources獲取= FetchType.LAZY?由於您明確加入,因此更改FetchType可能會有所幫助。 – arasul

回答

0

嘗試改變fetch=FetchType.EAGER對向的MessageResources fetch=FetchType.LAZY 既然你明確的加入,改變FetchTypeLAZY可能的幫助。

0

我不太明白你爲什麼期望inner join在這種情況下。

optional = "false"@ManyToOne意味着每個MessageResource必須有一個Language,但它並不意味着加載Language當每個Language必須至少有一個MessageResource,因此left join在這種情況下正確的 - 您加載可能沒有MessageResourceLanguage與它相關聯。

編輯:find()的目的是加載具有給定ID的對象,如果它存在於數據庫中。如果您希望語義上有所不同(例如,加載至少有一個MessageResource的所有Language),則需要爲其編寫查詢,如select l from Language l join l.messageResources

+0

謝謝你的澄清。那麼我應該如何配置註釋,以便強制它進行INNER JOIN呢? – JavaJuggler

+0

@JavaJuggler:我想你不能。在這種情況下爲什麼你需要'內部連接',對我來說沒有意義? – axtavt

+0

該偏好有很多原因。其中之一是INNER JOIN和LEFT OUTER JOIN之間的功能差異。我不希望關係不存在的結果。我嚴重懷疑JPA不允許兩個表之間的INNER JOIN ... – JavaJuggler

0

通過使用標準註釋來強制生成內部查詢(JPA是一個標準,並且您正在使用實現提供程序(例如,Hibernate,TopLink,Open JPA等))似乎沒有標準方法。不同的JPA實現使用不同的連接策略。欲瞭解更多詳情,請通過thisthis

+0

謝謝你的答案。我在今天的研究中已經閱讀過這些文章。他們都使用我想避免的「技巧」。第一個使用JPA-QL/Criteria API。第二個使用子實例調用實體管理器...我很驚訝內部連接不是直接的 – JavaJuggler

1

我不確定它是否是實際的解決方案,但我的渴望獲取經驗很糟糕。它總是以某種方式提取我不期望的。

您所查詢的是奇怪的,它不會使您同時選擇語言的MessageResource感

你可以試試:

語言對的MessageResource關係刪除fetch=FetchType.EAGER,並更改查詢東西

select l from Language l join fetch l.messageResources where .... 

它應該爲您提供該實例的語言和聚合消息資源,全部在一個SQL中。

有些問題,我想知道爲什麼你的MessageResource.language是insertable=false, updatable=false。看起來與在語言中矛盾,您指定了由該字段映射的關係,但您現在將其設置爲不可插入/可更新。

+0

我在JVM啓動時獲得所有的語言資源,所以我想用一個單一的鏡頭加載它們內部連接沒有任何where子句。關於可插入屬性和可更新屬性,他們不會介意。我把它們放在了Hibernate/JPA的重複列投訴中,我已經找出了抱怨動機並修正了它(即刪除了這些屬性)。作爲最後一點,這個查詢很有意義,因爲我在語言中有語言鍵(即。「en_US」,「es_ES」)和MessageResource中的鍵/值(資源包支持表被標準化) – JavaJuggler

+0

切換到FetchType.LAZY做了竅門,但我不同意你說這個查詢沒有有道理(正如我在之前的評論中所解釋的)。我保留了我使用的完全相同的查詢 – JavaJuggler

+0

@JavaJuggler事實上,我的方式允許你做你正在尋找的東西:以單一鏡頭加載所有消息資源。既然你已經映射了語言和MessageResource之間的關係,那麼你需要的僅僅是從'語言'加入獲取l.messageResources'獲得所有語言,就這些了。結果列表中的每種語言都具有相應的消息資源列表。你需要做的只是一個額外的按摩數據到任何你想要的形式。 –