10.繼承映射(hibernate筆記)

一、繼承映射的三種策略:

  • 單表繼承。一棵類繼承樹使用一個表(table per class hierachy)
  • 具體表繼承。每個子類一個表(table per subclass)
  • 類表繼承。每個具體類一個表(table per concrete class)(有一些限制)
    三種策略中的對象模型都是一樣的,如下:
對象模型.png

二、一棵繼承樹映射成一張表(工程hibernate_extends_1

如:_animal

id name sex weight height type
1 true 100 P
2 false 50 B

2.1、理解如何映射

  • 因為類繼承樹肯定是對應多個類,要把多個類的信息存放在一張表中,必須有某種機制來區(qū)分哪些記錄是屬于哪個類的。

  • 這種機制就是,在表中添加一個字段(type),用這個字段的值來進行區(qū)分。用hibernate實現(xiàn)這種策略的時候,有如下步驟:

    • 父類用普通的<class>標簽定義
    • 在父類中定義一個discriminator,即指定這個區(qū)分的字段的名稱和類型
      如:<discriminator column=”XXX” type=”string”/>
    • 子類使用<subclass>標簽定義,在定義subclass的時候,需要注意如下幾點:1.Subclass標簽的name屬性是子類的全路徑名;
      2.在Subclass標簽中,用discriminator-value屬性來標明本子類的discriminator字段(用來區(qū)分不同類的字段)的值Subclass標簽,既可以被class標簽所包含(這種包含關系正是表明了類之間的繼承關系),也可以與class標簽平行。 當subclass標簽的定義與class標簽平行的時候,需要在subclass標簽中,添加extends屬性,里面的值是父類的全路徑名稱。子類的其它屬性,像普通類一樣,定義在subclass標簽的內(nèi)部。

2.2、理解如何存儲

  • 存儲的時候hibernate會自動將鑒別字段值插入到數(shù)據(jù)庫中,在加載數(shù)據(jù)的時候,hibernate能根據(jù)這個鑒別值正確的加載對象.

  • 多態(tài)查詢:在hibernate加載數(shù)據(jù)的時候能鑒別出正真的類型(instanceOf),請查看測試類中的各個方法。

    • get支持多態(tài)查詢
    • load只有在lazy=false,才支持多態(tài)查詢
    • hql支持多態(tài)查詢

相關實體類:
Animal.java

private int id;
private String name;
private boolean sex;

Pig.java

private int weight;

Bird.java

private int height;

配置:
extends.java

<?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 package="cn.itcast.hibernate">
    <class name="Animal" table="_animal" >
        <id name="id">
            <generator class="native"/>
        </id>
        <!-- 定義一個type用來區(qū)分子類 -->
        <discriminator column="type" type="string"/>
        <property name="name"/>
        <property name="sex"/>
        <subclass name="Pig" discriminator-value="P">
            <property name="weight"/>
        </subclass>
        <subclass name="Bird" discriminator-value="B">
            <property name="height"/>
        </subclass>
    </class>
</hibernate-mapping>

說明:這里會生成一張表,即表_animal。注意type的類型是hibernate的string類型,不是java的String類型。

測試:
ExtendsTest.java

package cn.itcast.hibernate;
import java.util.Iterator;
import java.util.List;
import org.hibernate.Session;
import junit.framework.TestCase;
public class ExtendsTest extends TestCase {

    public void testSave1() {
        Session session = null;
        try {
            session = HibernateUtils.getSession();
            session.beginTransaction();
            
            Pig pig = new Pig();
            pig.setName("豬");
            pig.setSex(true);
            pig.setWeight(100);
            session.save(pig);
            
            Bird bird = new Bird();
            bird.setName("鳥");
            bird.setSex(false);
            bird.setHeight(50);
            session.save(bird);
            
            session.getTransaction().commit();
        }catch(Exception e) {
            e.printStackTrace();
            session.getTransaction().rollback();
        }finally {
            HibernateUtils.closeSession(session);
        }
    }   
    
    /**
     * 采用load,通過Pig查詢
     */
    public void testLoad1() {
        Session session = null;
        try {
            session = HibernateUtils.getSession();
            session.beginTransaction();
            
            Pig pig = (Pig)session.load(Pig.class, 1);
            System.out.println(pig.getName());
            
            session.getTransaction().commit();
        }catch(Exception e) {
            e.printStackTrace();
            session.getTransaction().rollback();
        }finally {
            HibernateUtils.closeSession(session);
        }
    }
    
    /**
     * 采用load,通過Animal查詢
     */
    public void testLoad2() {
        Session session = null;
        try {
            session = HibernateUtils.getSession();
            session.beginTransaction();
            
            Animal animal = (Animal)session.load(Animal.class, 1);
            System.out.println(animal.getName());
            
            session.getTransaction().commit();
        }catch(Exception e) {
            e.printStackTrace();
            session.getTransaction().rollback();
        }finally {
            HibernateUtils.closeSession(session);
        }
    }           
    
    /**
     * 采用load,通過Animal查詢
     */
    public void testLoad3() {
        Session session = null;
        try {
            session = HibernateUtils.getSession();
            session.beginTransaction();
            
            Animal animal = (Animal)session.load(Animal.class, 1);
            
            //因為load默認支持lazy,因為我們看到的是Animal的代理對象
            //所以通過instanceof是反應不出正真的對象類型的
            //因此load在默認情況下是不支持多態(tài)查詢的
            if (animal instanceof Pig) {
                System.out.println(animal.getName());
            }else {
                System.out.println("不是豬");
            }
            session.getTransaction().commit();
        }catch(Exception e) {
            e.printStackTrace();
            session.getTransaction().rollback();
        }finally {
            HibernateUtils.closeSession(session);
        }
    }           
    
    /**
     * 采用load,通過Animal查詢,將<class>標簽上的lazy=false
     */
    public void testLoad4() {
        Session session = null;
        try {
            session = HibernateUtils.getSession();
            session.beginTransaction();
            
            Animal animal = (Animal)session.load(Animal.class, 1);
            //可以正確的判斷出Pig的類型,因為lazy=false,返回的是具體的Pig類型
            //此時load支持多態(tài)查詢
            if (animal instanceof Pig) {
                System.out.println(animal.getName());
            }else {
                System.out.println("不是豬");
            }
            session.getTransaction().commit();
        }catch(Exception e) {
            e.printStackTrace();
            session.getTransaction().rollback();
        }finally {
            HibernateUtils.closeSession(session);
        }
    }       
    
    /**
     * 采用get,通過Animal查詢
     */
    public void testLoad5() {
        Session session = null;
        try {
            session = HibernateUtils.getSession();
            session.beginTransaction();
            
            //可以正確的判斷出Pig的類型,因為返回的是具體的Pig類型
            //get支持多態(tài)查詢
            Animal animal = (Animal)session.get(Animal.class, 1);

            if (animal instanceof Pig) {
                System.out.println(animal.getName());
            }else {
                System.out.println("不是豬");
            }
            session.getTransaction().commit();
        }catch(Exception e) {
            e.printStackTrace();
            session.getTransaction().rollback();
        }finally {
            HibernateUtils.closeSession(session);
        }
    }   
    
    /**
     * 采用get,通過Animal查詢
     */
    public void testLoad6() {
        Session session = null;
        try {
            session = HibernateUtils.getSession();
            session.beginTransaction();
//          List animalList = session.createQuery("from Animal").list();
//          for (Iterator iter = animalList.iterator(); iter.hasNext();) {
//              Animal a = (Animal)iter.next();
//              //能夠正確的鑒別出正真的類型,hql是支持多態(tài)查詢的
//              if (a instanceof Pig) {
//                  System.out.println("是Pig");
//              }else if (a instanceof Bird) {
//                  System.out.println("是bird");
//              } 
//          }
            
            List list = session.createQuery("from java.lang.Object").list();
            for (Iterator iter=list.iterator(); iter.hasNext();) {
                Object o = iter.next();
                if (o instanceof Pig) {
                    System.out.println("是Pig");
                }else if (o instanceof Bird) {
                    System.out.println("是bird");
                } 
            }
            session.getTransaction().commit();
        }catch(Exception e) {
            e.printStackTrace();
            session.getTransaction().rollback();
        }finally {
            HibernateUtils.closeSession(session);
        }
    }   
}

說明:

  • 1.我們可以通過Animal查出相關的子類信息,如方法testLoad2。
  • 2.對于方法testLoad3,我們可以看到在lazy默認情況下,我們是不能通過Animal反映出真正的對象類型。也就是load在默認情況下是不支持多態(tài)查詢的。
  • 3.采用load,通過Animal查詢,將<class>標簽上的lazy=false??梢钥吹皆诜椒?code>testLoad4中可以正確判斷子類的類型。
  • 4.在方法testLoad5中我們采用get方式通過Animal查詢,是可以返回具體的子類類型的。即get支持多態(tài)查詢。
  • 5.在方法testLoad6中我們使用hql語句進行查詢。這里我們試驗了兩種類型的查詢,可以看到hql是支持多態(tài)查詢的,因為返回的不是代理類,而是具體類。

三、每個子類映射成一張表(工程hibernate_extends_2

表繼承.JPG

說明:其中對象模型不變,只是關系模型變了,于是我們需要重新配置關系模型,而我們的存儲加載都不需要改變,而其存儲加載的效率不如第一種。而第一種也有缺點,里面有很多冗余字段,而且當將冗余字段設置為非空,那么是存儲不進去的。一般情況下采用第一種。

extends.hbm.xml

<?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 package="cn.itcast.hibernate">
    <class name="Animal" table="t_animal">
        <id name="id">
            <generator class="native"/>
        </id>
        <property name="name"/>
        <property name="sex"/>
        <joined-subclass name="Pig" table="_pig">
            <key column="pid"/>
            <property name="weight"/>
        </joined-subclass>
        <joined-subclass name="Bird" table="_bird">
            <key column="bid"/>
            <property name="height"/>
        </joined-subclass>
    </class>
</hibernate-mapping>

說明:此時會在數(shù)據(jù)庫中生成三張表。測試的時候和上面例子是一樣的,這里不多說。

四、每個具體類映射成一張表(工程hibernate_extends_3

數(shù)據(jù)庫表.JPG

說明:每個具體類映射成一張表,對象模型還是沒有變化。如果使用uuid則相關的存儲加載不需要改變,但是如果使用assigned手動分配則存儲加載需要做相應的改動,手動設置主鍵。

extends.hbm.xml

<?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 package="cn.itcast.hibernate">
    <class name="Animal" abstract="true">
        <id name="id">
            <generator class="assigned"/>
        </id>
        <property name="name"/>
        <property name="sex"/>
        <union-subclass name="Pig" table="_pig">
            <property name="weight"/>
        </union-subclass>
        <union-subclass name="Bird" table="_bird">
            <property name="height"/>
        </union-subclass>
    </class>
</hibernate-mapping>

說明:此時我們可以看到Animal就不會生成一張表了。同樣相關的測試都和上面例子一樣,這里不多說。注意映射方式的不同。

最后:比較來看,最后一種方式不能使用自增主鍵。一般推薦使用第一種。同時我們最后一種方式中需要將animal表設置成abstract="true",這樣animal表就不會生成出來,同時不會影響到存儲和加載。

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內(nèi)容

  • 1. Java基礎部分 基礎部分的順序:基本語法,類相關的語法,內(nèi)部類的語法,繼承相關的語法,異常的語法,線程的語...
    子非魚_t_閱讀 31,765評論 18 399
  • 對象的狀態(tài) Hibernate中對象的狀態(tài) : 臨時/瞬時狀態(tài)、持久化狀態(tài)、游離狀態(tài)臨時狀態(tài)特點:直接new出來的...
    奮斗的老王閱讀 941評論 0 49
  • 面向對象主要針對面向過程。 面向過程的基本單元是函數(shù)。 什么是對象:EVERYTHING IS OBJECT(萬物...
    sinpi閱讀 1,091評論 0 4
  • Hibernate: 一個持久化框架 一個ORM框架 加載:根據(jù)特定的OID,把一個對象從數(shù)據(jù)庫加載到內(nèi)存中OID...
    JHMichael閱讀 1,995評論 0 27
  • 還是孩子,母親病逝 少年錦時,祖母自盡 給我留下的只有那個漂亮的女子,就是那個陪伴我一生的妻子 1994年,好像提...
    舞化城閱讀 714評論 4 11