最簡單的方式就是給每個物件一個表格,如果父類別User中有field1、field2兩個屬性,其表格USER有FIELD1、FIELD2與之對應,而子類別SubUser若繼承了父類別的field1、field2屬性,表格中SUBUSER中也要擁有FIELD1、FIELD2與之對應,這種方法的好處只有映射上的方便,很顯然的,父類與子類共有的屬性,會變成在資料庫表格中重複的欄位,而且很難實現多型操作,建議只有在不需要多型操作時使用,要執行這種映射,為每一個子類別撰寫一個映射文件就是了,沒什麼特別的設定。
第二種方式是將所有繼承同一父類別的物件儲存在同一個表格中,表格中使用識別欄位來表示某一列(row)是屬於某個子類別或父類別,這種方式方便執行多型操作,而且兼具效能上的考量,在這個主題中我們將先說明這個方法。
我們先來看看我們撰寫的類別與繼承關係,首先是父類別:
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/1327ab569c1ae82736693a50b8e33378.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/7ff8d92cded7e0ce15e7ca1acc870052.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/717446ca04a6125dc5b6b54e0fa14ab4.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/7ff8d92cded7e0ce15e7ca1acc870052.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/717446ca04a6125dc5b6b54e0fa14ab4.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/7ff8d92cded7e0ce15e7ca1acc870052.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/717446ca04a6125dc5b6b54e0fa14ab4.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/7ff8d92cded7e0ce15e7ca1acc870052.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/717446ca04a6125dc5b6b54e0fa14ab4.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/7ff8d92cded7e0ce15e7ca1acc870052.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/717446ca04a6125dc5b6b54e0fa14ab4.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/7ff8d92cded7e0ce15e7ca1acc870052.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/717446ca04a6125dc5b6b54e0fa14ab4.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/0196c3df5ea9e936f21e9932cca91014.gif)
再來是繼承User類別的兩個子類別,首先是PowerUser類別:
package onlyfun.caterpillar;
public class PowerUser extends User {
private int level;
private String otherOfPower;
public int getLevel() {
return level;
}
public String getOtherOfPower() {
return otherOfPower;
}
public void setLevel(int level) {
this.level = level;
}
public void setOtherOfPower(String otherOfPower) {
this.otherOfPower = otherOfPower;
}
}
package onlyfun.caterpillar;
public class GuestUser extends User {
private String otherOfGuest;
public String getOtherOfGuest() {
return otherOfGuest;
}
public void setOtherOfGuest(String otherOfGuest) {
this.otherOfGuest = otherOfGuest;
}
}
映射文件中該如何撰寫,由於這些類別將映射至同一個表格,我們使用discriminator作為每個類別記錄在表格中的識別,先直接看看映射文件如何撰寫:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping
PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">
<hibernate-mapping>
<class name="onlyfun.caterpillar.User" table="USER" discriminator-value="ParentUser">
<id name="id" type="string" unsaved-value="null">
<column name="ID" sql-type="char(32)"/>
<generator class="uuid.hex"/>
</id>
<discriminator column="DISCRIMINATOR_USERTYPE" type="string"/>
<property name="name" type="string" not-null="true">
<column name="NAME" length="16" not-null="true"/>
</property>
<property name="password" type="string" not-null="true">
<column name="PASSWORD" length="16" not-null="true"/>
</property>
<subclass name="onlyfun.caterpillar.PowerUser" discriminator-value="POWER">
<property name="level" type="integer" column="POWERUSER_LEVEL"/>
<property name="otherOfPower" type="string" column="POWER_OTHER"/>
</subclass>
<subclass name="onlyfun.caterpillar.GuestUser" discriminator-value="GUEST">
<property name="otherOfGuest" type="string" column="GUEST_OTHER"/>
</subclass>
</class>
</hibernate-mapping>
在表格中,我們增加一個欄位DISCRIMINATOR_USERTYPE來記錄儲存的類別是屬於User、PowerUser或是 GuestUser的記錄,如果該欄位是ParentUser,則表示該筆資料是User類別,如果是POWER,表示是PowerUser的記錄,如果是GUEST,表示是GuestUser的記錄,在映射子類別時,使用<subclass>指明映射的子類別以及其 discriminator-value。
我們可以在資料庫中建立資料表格如下:
create table USER (
ID char(32) not null,
DISCRIMINATOR_USERTYPE varchar(255) not null,
NAME varchar(16) not null,
PASSWORD varchar(16) not null,
POWERUSER_LEVEL integer,
POWER_OTHER varchar(255),
GUEST_OTHER varchar(255),
primary key (ID)
);
您可以將資料表的建立工作,透過SchemaExportTask來自動建立,您可以參考這篇介紹:
使用SchemaExportTask
假設我們在程式中如下儲存資料的話:
PowerUser pu = new PowerUser();
pu.setName("caterpillar");
pu.setPassword("123456");
pu.setLevel(1);
pu.setOtherOfPower("PowerUser's field");
GuestUser gu = new GuestUser();
gu.setName("momor");
gu.setPassword("654321");
gu.setOtherOfGuest("GuestUser's field");
Session session = sessionFactory.openSession();
Transaction tx= session.beginTransaction();
session.save(pu);
session.save(gu);
tx.commit();
session.close();
則資料表中將會有以下的內容(沒有顯示ID欄位):
+------------------------+-------------+----------+-----------------+-------------------+-------------------+
| DISCRIMINATOR_USERTYPE | NAME | PASSWORD | POWERUSER_LEVEL | POWER_OTHER | GUEST_OTHER |
+------------------------+-------------+----------+-----------------+-------------------+-------------------+
| POWER | caterpillar | 123456 | 1 | PowerUser's field | NULL |
| GUEST | momor | 654321 | NULL | NULL | GuestUser's field |
+------------------------+-------------+----------+-----------------+-------------------+-------------------+
您可以觀察實際的儲存方式,注意DISCRIMINATOR_USERTYPE欄位,它用以標示該列屬於哪一個類別的資料,如果要查詢資料的話,例如查詢所有PowerUser的資料,我們只要如下進行:
Session session = sessionFactory.openSession();
List users = session.find("from PowerUser");
session.close();
for (ListIterator iterator = users.listIterator(); iterator.hasNext(); ) {
PowerUser user = (PowerUser) iterator.next();
System.out.println(user.getName() +
"/n/tPassword: " + user.getPassword());
System.out.println("/tPower: " + user.getOtherOfPower() +
"/n/tLevel: " + user.getLevel());
}
您可以觀察Hibernate真正所執行的SQL的內容,看看where就知道它如何查詢PowerUser的資料:
{code:borderStyle=solid}select poweruser0_.ID as ID,
poweruser0_.POWERUSER_LEVEL as POWERUSE5_,
poweruser0_.POWER_OTHER as POWER_OT6_,
poweruser0_.NAME as NAME,
poweruser0_.PASSWORD as PASSWORD
from USER poweruser0_ where poweruser0_.DISCRIMINATOR_USERTYPE='POWER';
使用session.find("from GuestUser");就可以查詢GuestUser的資料,您也可以取回所有User型態的資料,例如:
Session session = sessionFactory.openSession();
List users = session.find("from User");
session.close();
for (ListIterator iterator = users.listIterator(); iterator.hasNext(); ) {
User user = (User) iterator.next();
System.out.println(user.getName() +
"/n/tPassword: " + user.getPassword());
if(user instanceof PowerUser)
System.out.println("/tPower: " + ((PowerUser)user).getOtherOfPower() +
"/n/tLevel: " + ((PowerUser)user).getLevel());
else
System.out.println("/tGuest: " + ((GuestUser)user).getOtherOfGuest());
}
Hibernate可以使用父類別來取得所有的子類別資料,我們知道所有的Java類別都繼承自Object,所以如果您使用session.find("from java.lang.Object");,就將會取回資料庫中所有表格的資料。
有關於繼承關係映射的第三種作法,將留待下一個主題說明。