2015年11月13日 星期五

繼承映射二(table per subclass) (Hibernate3.x 十七)

※table per subclass(joined-subclass)

※準備資料

DROP TABLE STUDENT;
DROP TABLE MAKER;
DROP TABLE CHESS;
    
CREATE TABLE CHESS (
    ID NUMBER(5),
    NAME VARCHAR(10),
    PRICE NUMBER(5),
    PRODUCT_DATE DATE,
    CONSTRAINT PK_CHESS PRIMARY KEY(ID)
);
    
CREATE TABLE STUDENT(
    ID NUMBER(5),
    SCORE NUMBER(3),
    SCHOOL VARCHAR(20),
    CONSTRAINT PK_STUDENT PRIMARY KEY(ID),
    CONSTRAINT FK_STUDENT_ID FOREIGN KEY(ID) REFERENCES CHESS(ID) ON DELETE CASCADE
);
    
CREATE TABLE MAKER(
    ID NUMBER(5),
    SALARY NUMBER(6),
    COMPANY VARCHAR(20),
    CONSTRAINT PK_MAKER PRIMARY KEY(ID),
    CONSTRAINT FK_MAKER_ID FOREIGN KEY(ID) REFERENCES CHESS(ID) ON DELETE CASCADE
);

※和table per concrete class相比較,差在多一張CHESS表

※XML設定

Chess.java

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

※Chess.java、Student.java、Maker.java和table per concrete class (Hibernate3.x 十六)一樣

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

<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>
    
<joined-subclass name="vo.Student" table="STUDENT">
    <key column="id" />
    <property name="score" type="java.lang.Integer">
        <column name="SCORE" />
    </property>
    <property name="school" type="java.lang.String">
        <column name="SCHOOL" />
    </property>
</joined-subclass>
    
<joined-subclass name="vo.Maker" table="MAKER">
    <key column="id" />
    <property name="salary" type="java.lang.Integer">
        <column name="SALARY" />
    </property>
    <property name="company" type="java.lang.String">
        <column name="COMPANY" />
    </property>
</joined-subclass>

※和table per concrete class的union-subclass很像,將union-subclass改成joined-class,然後將對應的key(也就是FK)寫出來即可,一定要放在第一行

測試類別

SessionFactory sf = HibernateUtil2.getSessionFactory();
Session s1 = sf.openSession();
Session s2 = sf.openSession();
Transaction tx1 = 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);
    tx1.commit();
    
    Student stuRtn = (Student) s1.get(Student.class, 1);
    System.out.println("學校名:" + stuRtn.getSchool());
    
    System.out.println("-----測試員工-----");
    Maker mak = new Maker();
    mak.setId(2);
    mak.setName("五子棋");
    mak.setPrice(30);
    mak.setProductDate(new Date(102, 11, 12));
    mak.setSalary(43000);
    mak.setCompany("少林寺管委會");
    s2.save(mak);
    tx2.commit();
    
    Maker makRtn = (Maker) s2.load(Maker.class, 2);
    System.out.println("公司名:" + makRtn.getCompany());
} catch (Exception e) {
    System.err.println("例外錯誤!");
    e.printStackTrace();
    tx1.rollback();
    tx2.rollback();
} finally {
    if (s1.isOpen()) {
        s1.close();
    }
    if (s2.isOpen()) {
        s2.close();
    }
}

※hibernate.cfg.xml只要多一行<mapping resource="vo/Chess.hbm.xml" />

※catch裡的rollback如果寫在第一行,在commit之後有錯,會到catch,執行完rollback後會直接到finally,編譯居然沒問題,也就是catch後面的程式碼沒執行
如果load/get一個沒有記錄的資料會null或exception,然後null在「.」也是exception,這時因為rollback在第一行,所以錯在哪一行和訊息沒有印出來,只會出現「org.hibernate.TransactionException: Transaction not successfully started」,而且資料還新增成功,因為已經先commit,後面才出錯

修改成兩個表一次commit

Session s = HibernateUtil2.getSession();
Transaction tx = s.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("家裡蹲大學");
    s.save(stu);
    
    Student stuRtn = (Student) s.get(Student.class, 1);
    System.out.println("學校名:" + stuRtn.getSchool());
    
    System.out.println("-----測試員工-----");
    Maker mak = new Maker();
    mak.setId(2);
    mak.setName("五子棋");
    mak.setPrice(30);
    mak.setProductDate(new Date(102, 11, 12));
    mak.setSalary(43000);
    mak.setCompany("少林寺管委會");
    s.save(mak);
    
    Maker makRtn = (Maker) s.load(Maker.class, 2);
    System.out.println("公司名:" + makRtn.getCompany());
    
    tx.commit();
} catch (Exception e) {
    System.err.println("例外錯誤!");
    e.printStackTrace();
    tx.rollback();
    tx.rollback();
} finally {
    if (s.isOpen()) {
        s.close();
    }
}

※雖然解決了一次commit的問題,但尤於key是FK,所以Student和Maker的key不能重復,這是沒辦法的

※注意控制台出的select訊息是select xxx from Student student0_, CHESS student0_1_ ,所以是join,會有效能慢的問題


※Annotation設定

hibernate.cfg.xml

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

Chess.java

@Inheritance(strategy = InheritanceType.JOINED)
@Entity
@Table(name = "CHESS")
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因不同,所以要打

Student.java

@Inheritance(strategy = InheritanceType.JOINED)
@Entity
public class Student extends Chess {
    //...
}

※因為Column和資料庫全同名,所以只要設定class上頭的annotation

Maker.java

@Inheritance(strategy = InheritanceType.JOINED)
@Entity
public class Maker extends Chess {
    //...
}

※因為Column和資料庫全同名,所以只要設定class上頭的annotation

※測試類一樣

沒有留言:

張貼留言