2012年3月17日 星期六

Hibernate 教學 - 繼承 : Table per concrete class with unions

本篇介紹 Hibernate 中的繼承機制裡的其中一個  Table per concrete class with unions

從字面上翻譯,可以看得出一個table會對應到一個具體類

但是它不像 Table per concrete class with implicit polymorphism 需要一個具體類別就要一個映射檔

Table per concrete class with unions  只需要一個映射檔並透過 union-subclass 

來指名具體類是對映到哪個資料表即可

以下以一個"優惠(Preferential)"為一個抽象的父類別,並有兩個子類別

"折扣(Discount)" 以及 "免費(Free)"

首先先來看這三個簡單的Java程式

Preferential.java

package org.pojo;
public abstract  class Preferential 
{
    private int id;    //共同屬性 id  
    private String name;   //共同屬性 name
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}
Discount.java
package org.pojo;
public class Discount extends Preferential 
{
    private int percent;

    public int getPercent() {
        return percent;
    }

    public void setPercent(int percent) {
        this.percent = percent;
    }
}

Free.java
package org.pojo;

public class Free extends Preferential 
{
    private int count;
    public int getCount() {
        return count;
    }
    public void setCount(int count) {
        this.count = count;
    }
}

接著是資料表的結構,一個實體類匯對應一個表 

Discount.java > t_discount        Free.java > t_free











可以看到 id 和 name 都是定義在 Preferential.java裡,因為是共有的

但是你的每個實體資料表則必須都定義的

接著在這種 Table per concrete class with unions 方法中,我們只需要使用一個映射文件檔

Preferential.hbm.xml


<hibernate-mapping>
     <!-- abstract true 表示 Preferential 為抽象類別 -->
     <class name="org.pojo.Preferential" abstract="true">
           <!-- 共有屬性 -->
           <id name="id" column="id">
               <generator class="increment" /> <!-- 自增 -->
           </id>
          
           <!-- 共有屬性 -->
           <property name="name" />
          
           <!-- union-subclass來指名實體纇,及其對應的資料表 -->
           <union-subclass name="org.pojo.Discount" table="t_discount">
                 <!-- 自有的屬性 -->
               <property name="percent" column="percent"/>
           </union-subclass>
          
           <union-subclass name="org.pojo.Free" table="t_free">
               <property name="count" column="count"/>
           </union-subclass>
     </class>
</hibernate-mapping>


到這邊為止你的繼承工作就完成了,最後別忘了到hibernate.cfg.xml或是hibernate.properties

註冊這個映射檔(Preferential.hbm.xml)

最後來測試一下新增一筆折扣以及一筆免費的過程

以下是JUnit的片段程式

public void testInsert()
{
    Session session = HibernateUtil.getSessionFactory().openSession(); 
    Transaction tx= session.beginTransaction(); 
    Discount discount = new Discount();
    Free free = new Free();
        
    discount.setName("商品A折扣");
    discount.setPercent(75); //75折
        
    free.setName("商品A免費");
    free.setCount(1); //買一送一
        
    session.save(discount);
    session.save(free);
    tx.commit(); 
    session.close(); 
}
如果執行成功你會看到 Hibernate 會下3道SQL,為什麼呢?

Hibernate: select max(ids_.id) from ( select id from t_discount union select id from t_free ) ids_
Hibernate: insert into t_discount (name, percent, id) values (?, ?, ?)
Hibernate: insert into t_free (name, count, id) values (?, ?, ?)

原因是因為兩個資料表的主健都是採用自動遞增的方式,但是id屬性是共通的

它被定義在抽象類別Preferential上,因此Hibernate必須先找出雙方最大的id才能繼續Insert下去

如果今天要找出所有優惠(Preferential)的資料可以如下:

Query query = session.createQuery("from org.pojo.Preferential");
for(Iterator it =  query.list().iterator();it.hasNext();){
    Preferential preferential = (Preferential)it.next();
    System.out.println(preferential.getName());
}

如果想要找出所有折扣(Discount)的資料可以如下:

Query query = session.createQuery("from org.pojo.Discount");
for(Iterator it =  query.list().iterator();it.hasNext();){
    Discount discount = (Discount)it.next();
    System.out.println(discount.getName());
}
當然如果想要找出所有的 免費(Free)的資料,就如上依此類推了

沒有留言:

張貼留言