2015年11月8日 星期日

繼承映射一(table per concrete class) (Hibernate3.x 十六)

繼承映射分成三種形式
※table per class hierarchy(每個階層類別一個表)
※table per subclass(每個子類別一個表)
※table per concrete class(每個具體類別一個表)
尤於翻譯的有和沒有一樣,所以提供官方連結,這裡先從下往上講,個人感覺比較好懂

※table per concrete class(union-subclass)


※準備資料

CREATE TABLE STUDENT(
    ID NUMBER(5),
    NAME VARCHAR(10),
    PRICE NUMBER(5),
    PRODUCT_DATE DATE,
    SCORE NUMBER(3),
    SCHOOL VARCHAR(20),
    CONSTRAINT PK_STUDENT PRIMARY KEY(ID)
);
    
CREATE TABLE MAKER(
    ID NUMBER(5),
    NAME VARCHAR(10),
    PRICE NUMBER(5),
    PRODUCT_DATE DATE,
    SALARY NUMBER(6),
    COMPANY VARCHAR(20),
    CONSTRAINT PK_MAKER PRIMARY KEY(ID)
);

※ID、NAME、PRICE、PRODUCT_DATE,這四個欄位兩張表都有,所以可以提出來模擬成java的繼承關係,取一個名字,譬如叫Chess,然後將兩張對應的表繼承它


※XML設定

Chess.java

public abstract class Chess {
    private Integer id;
    private String name;
    private Integer price;
    private Date productDate;
    //setter/getter...
}

※Chess是個抽象類別,讓Student和Maker繼承

Student.java

public class Student extends Chess {
    private Integer score;
    private String school;
    //setter/getter...
}

Maker.java
public class Maker extends Chess {
    private Integer salary;
    private String company;
    //setter/getter...
}

Chess.hbm.xml
<class name="vo.Chess" abstract="true">
    <id name="id" type="java.lang.Integer">
        <column name="ID" />
        <generator class="assigned" />
    </id>
    
    <property name="name" type="java.lang.String">
        <column name="NAME" />
    </property>
    
    <property name="price" type="java.lang.Integer">
        <column name="PRICE" />
    </property>
    
    <property name="productDate" type="java.util.Date">
        <column name="PRODUCT_DATE" />
    </property>
    
    <union-subclass name="vo.Student" table="STUDENT">
        <property name="score" type="java.lang.Integer">
            <column name="SCORE" />
        </property>
        <property name="school" type="java.lang.String">
            <column name="SCHOOL" />
        </property>
    </union-subclass>
    
    <union-subclass name="vo.Maker" table="MAKER">
        <property name="salary" type="java.lang.Integer">
            <column name="SALARY" />
        </property>
        <property name="company" type="java.lang.String">
            <column name="COMPANY" />
        </property>
    </union-subclass>
</class>

※Chess可以加個abstract=true,更加確定是個抽象類別,其他兩個類別用union-subclass

測試類:

SessionFactory sf = HibernateUtil2.getSessionFactory();
Session s1 = sf.openSession();
Session s2 = sf.openSession();
Transaction tx = s1.beginTransaction();
Transaction tx2 = s2.beginTransaction();
try {
    System.out.println("-----測試學生-----");
    Student stu = new Student();
    stu.setId(1);
    stu.setName("象棋");
    stu.setPrice(50);
    stu.setProductDate(new Date(100, 0, 1));
    stu.setScore(78);
    stu.setSchool("家裡蹲大學");
    s1.save(stu);
    
    Student stuRtn = (Student) s1.load(Student.class, 1);
    System.out.println("學校名:" + stuRtn.getSchool());
    
    System.out.println("-----測試員工-----");
    Maker mak = new Maker();
    mak.setId(1);
    mak.setName("五子棋");
    mak.setPrice(30);
    mak.setProductDate(new Date(102, 11, 12));
    mak.setSalary(43000);
    mak.setCompany("少林寺管委會");
    s2.save(mak);
    
    Maker makRtn = (Maker) s2.load(Maker.class, 1);
    System.out.println("公司名:" + makRtn.getCompany());
    
    tx.commit();
    tx2.commit();
} catch (Exception e) {
    System.err.println("例外錯誤!");
    e.printStackTrace();
    tx.rollback();
} finally {
    if (s1.isOpen()) {
        s1.close();
    }
    if (s2.isOpen()) {
        s2.close();
    }
}

※hibernate.cfg.xml只要增加一行<mapping resource="vo/Chess.hbm.xml" />即可

※如果使用同一個session,而且id相同,就會出現「org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session:」的例外
就算有二級快取,也是一樣,所以和快取沒關係
.原因是因為同一個session不能有相同的名稱
.使用Session的clean、refresh、merge都不行
.但如果是Student和Maker分開,也就是Student做完就commit,然後clean再做Maker即可,我現在是假設二筆資料一起commit、一起rollback的話就不行
.Maker不使用save,改用merge會過,但在load時,會出現Student不能強轉成Maker的錯,很正常,因為繼承同一個類別,Session裡面存的是第一次的Student
.這是使用table per concrete class的缺點


※Annotation設定

Chess.java

@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
@Entity
public abstract class Chess {
    private Integer id;
    private String name;
    private Integer price;
    private Date productDate;
    
    @Id
    public Integer getId() {
        return id;
    }
    
    public void setId(Integer id) {
        this.id = id;
    }
    
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
    
    public Integer getPrice() {
        return price;
    }
    
    public void setPrice(Integer price) {
        this.price = price;
    }
    
    @Column(name = "PRODUCT_DATE")
    public Date getProductDate() {
        return productDate;
    }
    
    public void setProductDate(Date productDate) {
        this.productDate = productDate;
    }
}

※id是一定要設的,而Column只有productDate的名稱和資料庫不同,設這個就好

※三個類別都必需設定@Inheritance

Student.java

@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
@Entity
public class Student extends Chess {
    private Integer score;
    private String school;
    //setter/getter...
}

※由於Table和Column一樣可以不打,所以這是最精簡設定

Maker.java

@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
@Entity
public class Maker extends Chess {
    private Integer salary;
    private String company;
    //setter/getter...
}


hibernate.cfg.xml

<mapping class="vo.Chess" />
<mapping class="vo.Student" />
<mapping class="vo.Maker" />

※三個類別都必需增加才可以

※測試類別一樣

沒有留言:

張貼留言