一、繼承映射的三種策略:
- 單表繼承。一棵類繼承樹使用一個表(table per class hierachy)
- 具體表繼承。每個子類一個表(table per subclass)
- 類表繼承。每個具體類一個表(table per concrete class)(有一些限制)
三種策略中的對象模型都是一樣的,如下:
二、一棵繼承樹映射成一張表(工程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
)
說明:其中對象模型不變,只是關系模型變了,于是我們需要重新配置關系模型,而我們的存儲加載都不需要改變,而其存儲加載的效率不如第一種。而第一種也有缺點,里面有很多冗余字段,而且當將冗余字段設置為非空,那么是存儲不進去的。一般情況下采用第一種。
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
)
說明:每個具體類映射成一張表,對象模型還是沒有變化。如果使用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表就不會生成出來,同時不會影響到存儲和加載。