Company與Employee類之間為一對多多態關聯關系,如果繼承關系樹的根類對應一個表,或者每個類對應一個表,那麼就能映射Company類的employees集合。本節介紹如何映射多對一多態關聯。如圖14-11所示,ClassD與ClassA為多對一多態關聯關系。
假定與ClassD對應的表為TABLE_D,與ClassA對應的表為TABLE_A,在TABLE_D中定義了外鍵A_ID,它參照TABLE_A表的主鍵。
ClassD對象的a屬性既可以引用ClassB對象,也可以引用ClassC對象,例如:
tx = session.beginTransaction();
ClassD d=(ClassD)session.get("ClassD",id);
ClassA a=d.getA();
if(a instanceof ClassB)
System.out.println(((ClassB)a).getB1());
if(a instanceof ClassC)
System.out.println(((ClassC)a).getC1());
tx.commit();
以下代碼在映射ClassD類的a屬性時使用了延遲檢索策略:
<many-to-one name="a"
class="ClassA"
column="A_ID"
lazy="true"
cascade="save-update" />
當Hibernate加載ClassD對象時,它的屬性a引用ClassA的代理類實例,在這種情況下,如果對ClassA的代理類實例進行類型轉換,會拋出ClassCastException:
ClassA a=d.getA();
ClassB b=(ClassB)a; //拋出ClassCastException
解決以上問題的一種辦法是使用Session.load()方法:
ClassA a=d.getA();
ClassB b=(ClassB)session.load(ClassB.class,a.getId());
System.out.println(b.getB1());
當執行Session的load()方法時,Hibernate並不會訪問數據庫,而是僅僅返回ClassB的代理類實例。這種解決辦法的前提條件是必須事先知道ClassD對象實際上和ClassA的哪個子類的對象關聯。
解決以上問題的另一種辦法是顯式使用迫切左外連接檢索策略,避免Hibernate創建ClassA的代理類實例,而是直接創建ClassA的子類的實例:
tx = session.beginTransaction();
ClassD d=(ClassD)session.createCriteria(ClassD.class)
.add(Expression.eq("id",id))
.setFetchMode("a",FetchMode.EAGER)
.uniqueResult();
ClassA a=d.getA();
if(a instanceof ClassB)
System.out.println(((ClassB)a).getB1());
if(a instanceof ClassC)
System.out.println(((ClassC)a).getC1());
tx.commit();
如果繼承關系樹的具體類對應一個表,為了表達ClassD與ClassA的多態關聯,需要在TABLE_D中定義兩個字段:A_ID和A_TYPE,A_TYPE字段表示子類的類型,A_ID參照在子類對應的表中的主鍵。圖14-12顯示了表TABLE_D、TABLE_B和TABLE_C的結構。
圖14-12 表TABLE_D、TABLE_B和TABLE_C的結構