2010-04-12 107 views
6

我正在試驗Hibernate以獲得經驗。我創建了一個類Person有兩個子類:StudentWorkerHibernate:這個映射到外鍵上的子類的映射有什麼問題?

public abstract class Person { 
    private Long id; 
    ... 
} 

public class Student extends Person { ... } 

另一類,Employer,有Worker雙向一個一對多的關係。

public class Worker extends Person { 
    private Employer employer; 
    ... 
} 

public class Employer { 
    private String taxId; 
    private Set<Worker> employees = new HashSet<Worker>(); 
    ... 
} 

該映射是

<class name="Employer" table="EMPLOYER"> 
    <id name="taxId" column="TAX_ID" length="11"> 
     <generator class="assigned"/> 
    </id> 
    ... 
    <set name="employees" inverse="true"> 
     <key column="EMPLOYER_TAX_ID"/> 
     <one-to-many class="Worker"/> 
    </set> 
</class> 

繼承層次結構被建模與混合策略,其中Student映射到PERSON表,但Worker被存儲在它自己的表,加入用外鍵:

<class name="Person" table="PERSON"> 
    <id name="id" column="PERSON_ID" type="long" unsaved-value="0"> 
     <generator class="native"/> 
    </id> 
    <discriminator column="PERSON_TYPE" type="string"/> 
    ... 
    <subclass name="Student" discriminator-value="STU"> ... </subclass> 

    <subclass name="Worker" discriminator-value="WRK"> 
     <join table="WORKER"> 
      <key column="WORKER_ID"/> 
      <many-to-one name="employer" column="EMPLOYER_TAX_ID" cascade="save-update"/> 
      ... 
     </join> 
    </subclass> 
</class> 

我使用Apache Derby 10.5.3.0並通過設置自動生成模式0至create-drop

爲了測試這一切,我創建了下面的數據集DBUnit的測試:

<EMPLOYER TAX_ID   = "1234567890" 
      ... 
/> 
<PERSON PERSON_ID   = "12345" 
      PERSON_TYPE  = "WRK" 
      ... 
/> 
<WORKER WORKER_ID   = "12345" 
      EMPLOYER_TAX_ID = "1234567890" 
      ... 
/> 

我有它加載的工人實體並驗證它是否具有正確的員工進行測試。這通過。然後,對於相反方向上的測試:

String taxId = "1234567890"; 

    Employer employer = (Employer) session.get(Employer.class, taxId); 

    assertNotNull(employer); 
    assertThat(employer.getEmployees().size(), is(1)); 

在執行時,最後的斷言失敗,因爲組僱員爲空。

進一步挖掘,我發現,由於某些原因休眠表人看起來(並創建)的EMPLOYER_TAX_ID列,而不是WORKER的!它也存在於WORKER中,但該查詢中沒有使用它。用於填寫員工組的選擇語句爲:

select 
    employees0_.EMPLOYER_TAX_ID as EMPLOYER10_1_, 
    employees0_.PERSON_ID as PERSON1_1_, 
    employees0_.PERSON_ID as PERSON1_1_0_, 
    employees0_.FIRST_NAME as FIRST3_1_0_, 
    employees0_.FAMILY_NAME as FAMILY4_1_0_, 
    employees0_.DATE_OF_BIRTH as DATE5_1_0_, 
    employees0_.HOME_ADDRESS as HOME6_1_0_, 
    employees0_.CITY as CITY1_0_, 
    employees0_.ZIP as ZIP1_0_, 
    employees0_1_.EMPLOYER_TAX_ID as EMPLOYER2_2_0_, 
    employees0_1_.JOB_TITLE as JOB3_2_0_, 
    employees0_1_.JOB_GRADE as JOB4_2_0_, 
    employees0_1_.START_DATE as START5_2_0_ 
from 
    PERSON employees0_ 
inner join 
    WORKER employees0_1_ 
     on employees0_.PERSON_ID=employees0_1_.WORKER_ID 
where 
    employees0_.EMPLOYER_TAX_ID=? 

這是爲什麼? 我怎樣才能讓Hibernate在WORKER表中找到EMPLOYER_TAX_ID?

請注意,由於這是一個實驗項目,我可以改變任何事情。我讚賞任何解決方法,但我更願意瞭解正在進行的操作並儘可能修復此映射(儘可能)。

更新:如果我切換到一個乾淨的<joined-subclass>繼承映射策略,生成的模式看起來應該和測試通過。這是一個很好的解決方法,但我仍然好奇是否有辦法讓混合策略能夠正常工作。

+0

如果您可以使用JPA批註,爲什麼還要使用那些討厭的XML文件? – Kdeveloper 2010-04-12 12:43:33

+0

@Kdeveloper我們真正的項目使用XML,因此這是我的主要目標。但我會將我的寵物項目擴展到未來的註釋,因爲時間允許... – 2010-04-12 12:55:11

+1

僅供以後參考,將使用註釋定義一對多雙向關係,如下所示: '@OneToMany(mappedBy =「nameOfSingleInstanceInOtherEntity 「)',\t'@ManyToOne @JoinColumn(name =」nameWhichYouWishToGiveToIdField「)' – 2010-04-12 14:52:54

回答

2

這聽起來像已知的bug:http://opensource.atlassian.com/projects/hibernate/browse/HHH-1015

,因爲年齡已經知道,並已上報萬噸倍。不過,他們沒有修復它...

+0

順便說一下,的問題在於它會導致巨大的查詢。對於兩個子類來說並不那麼龐大,但是每個下一個子類都會顯着增加SQL大小。 – doublep 2010-04-18 19:08:14

+0

謝謝,這真的好像是一樣的情況! – 2010-04-18 19:42:15

0

我面臨同樣的問題,我用這種方式修復它:
通過聯合sublclass更改子類。
在你的情況將是:

<joined-subclass name="Worker" table="WORKER"> 
    <key column="WORKER_ID"/> 
    <many-to-one name="employer" column="EMPLOYER_TAX_ID" cascade="save-update"/> 
    ... 
</joined-subclass> 

希望這有助於。

+0

使用聯合子類意味着沒有辦法使用另一個無繼承策略。也許[沒有機會這樣做](http://docs.jboss.org/hibernate/orm/3.3/reference/en/html/inheritance.html#inheritance-mixing-tableperclass-tablepersubclass)。 – 2013-08-08 16:17:39