一、繼承映射
繼承是面向對像很重要的特性,它實現了代碼的服用,在關係模型中同樣也有繼承關係,這種繼承關係其實可以看做是一種枚舉關係,一種類型中可以枚舉出很多子類型,這些子類型和父對象形成了繼承關係,能夠對其進行枚舉的大部分都可以看做是一種繼承映射,所以這種枚舉關係可以看做是繼承映射,例如動物就是一種抽像類,它是其它動物豬、貓等的父類,它們之間就是一種繼承關係,如下圖:
這種繼承映射在轉化為關係模型後會生成一張表,那麼這張表是如何區分這兩種類型的呢?用的是關係字段,需要在表中添加類型字段,使用關鍵字來標明對象的類型。所以上圖中的對像模型對應的表結構如下:
在生成表結構時,需要添加對應的字段類型,所以需要在映射文件中添加對應的映射鑑別器,這裡就需要使用discriminator-value屬性。
1.類文件
類文件中沒有需要注意的地方,在編寫時注意之間的繼承關係即可。
清單一:Animal類代碼,只需要添加基本的屬性。
package com.src.hibernate; public class Animal { //id號private int id; public int getId() { return id; } public void setId(int id) { this.id = id; } //名稱private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } //性別private boolean sex; public boolean isSex() { return sex; } public void setSex(boolean sex) { this.sex = sex; } }清單二:Bird和Pig類,添加基本的屬性,並繼承Animal類。
package com.src.hibernate; public class Bird extends Animal { //高度private int height; public int getHeight() { return height; } public void setHeight(int height) { this.height = height; } } package com.src.hibernate; public class Pig extends Animal { //重量private int weight; public int getWeight() { return weight; } public void setWeight(int weight) { this.weight = weight; } } 2.映射文件
映射文件中需要添加對應的映射,該模型中只需要添加一個映射文件,因為只生成一張表,在映射文件中添加對應的子類映射,使用<subclass>標籤,標籤中添加鑑別器discriminator-value,該鑑別器屬性指明了在數據庫中寫入數據時指示寫入的是何種類型,如下:
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="com.src.hibernate.Animal" table="t_animal"> <id name="id"> <generator/> </id> <!-- 加入鑑別標籤,且必須放在id後面--> <discriminator column="type" /> <property name="name"/> <property name="sex" type="boolean"/> <subclass name="com.src.hibernate.Pig" discriminator-value="P"> <property name="weight"/> </subclass> <subclass name="com.src.hibernate.Bird" discriminator-value="B"> <property name="height"/> </subclass> </class> </hibernate-mapping>
3.分析結果
生成的MySQL數據庫表中不僅會添加Animal的基本屬性,而且會添加Pig和Bird的屬性,因為在映射文件中使用<subclass>寫出了所添加的屬性,另外還添加了相應的鑑別器屬性,所以在數據庫中會添加對應的鑑別列,生成的表結構如下圖:
二、數據操作
1.寫入數據
在進行數據讀取和寫入操作時需要注意類中的操作使用了
public void testSave(){ Session session=null; try{ //創建session對象session=HibernateUtils.getSession(); //開啟事務session.beginTransaction(); Pig pig=new Pig(); pig.setName("小豬豬"); pig.setSex(true); pig.setWeight(200); session.save(pig); Bird bird=new Bird(); bird.setName("xiaoniaoniao"); bird.setSex(true); bird.setHeight(100); session.save(bird); session.getTransaction().commit(); }catch(Exception e){ e.printStackTrace(); session.getTransaction().rollback(); }finally{ HibernateUtils.closeSession(session); } }2.多態查詢--get和hql
基本的查詢方法,只需要使用load和get方法即可,這裡重點討論多態查詢。多態查詢是指Hibernate在加載對象時能夠採用instanceof鑑別出其真正的類型的對象即可為多態查詢。
Note:多態查詢不支持延遲加載,也就是說如果使用load方法,需要在映射文件中將延遲加載設置為false。
3.load延遲加載
load支持延遲加載,在加載對象時其實生成的是對象的代理,所以在使用多態查詢時需要在映射文件中將延遲加載設置為false,如下:
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="com.src.hibernate.Animal" table="t_animal" lazy="false"> <id name="id"> <generator/> </id> <!-- 加入鑑別標籤,且必須放在id後面--> <discriminator column="type" /> <property name="name"/> <property name="sex" type="boolean"/> <subclass name="com.src.hibernate.Pig" discriminator-value="P"> <property name="weight"/> </subclass> <subclass name="com.src.hibernate.Bird" discriminator-value="B"> <property name="height"/> </subclass> </class> </hibernate-mapping>
load加載方法,使用load加載該示例中支持多態查詢,在配置文件中將延遲加載設置為false,所以這裡使用load方法能夠加載獲得相應的對像類。
public void testLoad(){ Session session=null; try{ session=HibernateUtils.getSession(); session.beginTransaction(); Animal ani=(Animal)session.load(Animal.class,1); System.out.println(ani.getName()); //因為load默認支持lazy,所以我們看到的是Animal的代理//所以採用了instanceof無法鑑別出真正的類型Pig //所以load在此情況下是不支持多態查詢的if(ani instanceof Pig){ System.out.println("我是小豬豬!"); }else{ System.out.println("我不是小豬豬!"); } session.getTransaction().commit(); }catch(Exception e){ e.printStackTrace(); session.getTransaction().rollback(); }finally{ HibernateUtils.closeSession(session); } } 4.hql查詢
hql支持多態查詢,這主要由於查詢出的是一個真正的對象,並不會返回一個代理,所以hql支持多態查詢,另外在查詢時需要注意查詢語句中不要使用表名,而是要使用類名稱,Hibernate會根據類名稱將其映射為對應的表名稱,如下:
public void testLoad5(){ Session session=null; try{ session=HibernateUtils.getSession(); session.beginTransaction(); List<Animal> list=session.createQuery("from Animal").list(); for(Iterator iter=list.iterator();iter.hasNext();){ Animal a=(Animal)iter.next(); if(a instanceof Pig){ System.out.println("我是小豬豬!"); }else{ System.out.println("我不是小豬豬!"); } } session.getTransaction().commit(); }catch(Exception e){ e.printStackTrace(); session.getTransaction().rollback(); }finally{ HibernateUtils.closeSession(session); } }查詢結果:
Hibernate: select animal0_.id as id0_, animal0_.name as name0_, animal0_.sex as sex0_, animal0_.weight as weight0_, animal0_.height as height0_, animal0_.type as type0_ from t_animal animal0_ 我是小豬豬! 我不是小豬豬! 我是小豬豬! 我不是小豬豬!
三、繼承映射三種策略
1. 每個類分層結構一張表(Table per class hierarchy)
假設我們有接口Payment和它的幾個實現類: CreditCardPayment, CashPayment, 和ChequePayment。則“每個類分層結構一張表”(Table per class hierarchy)的映射代碼如下所示:
<class name="Payment" table="PAYMENT"> <id name="id" type="long" column="PAYMENT_ID"> <generator/> </id> <discriminator column="PAYMENT_TYPE" type="string"/> <property name="amount" column="AMOUNT"/> ... <subclass name="CreditCardPayment" discriminator-value="CREDIT"> <property name="creditCardType" column="CCTYPE"/> ... </subclass> <subclass name="CashPayment" discriminator-value="CASH"> ... </subclass> <subclass name="ChequePayment" discriminator-value="CHEQUE"> ... </subclass> </class>
採用這種策略只需要一張表即可。它有一個很大的限制:要求那些由子類定義的字段, 如CCTYPE,不能有非空(NOT NULL)約束。
2. 每個子類一張表(Table per subclass)
對於上例中的幾個類而言,採用“每個子類一張表”的映射策略,代碼如下所示:
<class name="Payment" table="PAYMENT"> <id name="id" type="long" column="PAYMENT_ID"> <generator/> </id> <property name="amount" column="AMOUNT"/> ... <joined-subclass name="CreditCardPayment" table="CREDIT_PAYMENT"> <key column="PAYMENT_ID"/> ... </joined-subclass> <joined-subclass name="CashPayment" table="CASH_PAYMENT"> <key column="PAYMENT_ID"/> <property name="creditCardType" column="CCTYPE"/> ... </joined-subclass> <joined-subclass name="ChequePayment" table="CHEQUE_PAYMENT"> <key column="PAYMENT_ID"/> ... </joined-subclass> </class>
需要四張表。三個子類表通過主鍵關聯到超類表(因而關係模型實際上是一對一關聯)。
3. 每個子類一張表(Table per subclass),使用辨別標誌(Discriminator)
注意,對“每個子類一張表”的映射策略,Hibernate的實現不需要辨別字段,而其他的對象/關係映射工具使用了一種不同於Hibernate的實現方法,該方法要求在超類表中有一個類型辨別字段(type discriminator column)。 Hibernate採用的方法更難實現,但從關係(數據庫)這點上來看,按理說它更正確。若你願意使用帶有辨別字段的“每個子類一張表”的策略,你可以結合使用<subclass> 與<join>,如下所示:
<class name="Payment" table="PAYMENT"> <id name="id" type="long" column="PAYMENT_ID"> <generator/> </id> <discriminator column="PAYMENT_TYPE" type="string"/> <property name="amount" column="AMOUNT"/> ... <subclass name="CreditCardPayment" discriminator-value="CREDIT"> <join table="CREDIT_PAYMENT"> <property name="creditCardType" column="CCTYPE"/> ... </join> </subclass> <subclass name="CashPayment" discriminator-value="CASH"> <join table="CASH_PAYMENT"> ... </join> </subclass> <subclass name="ChequePayment" discriminator-value="CHEQUE"> <join table="CHEQUE_PAYMENT" fetch="select"> ... </join> </subclass> </class>
可選的聲明fetch="select",是用來告訴Hibernate,在查詢超類時, 不要使用外部連接(outer join)來抓取子類ChequePayment的數據。