一码倦、繼承映射的三種策略:
- 單表繼承袁稽。一棵類繼承樹使用一個(gè)表(table per class hierachy)
- 具體表繼承擒抛。每個(gè)子類一個(gè)表(table per subclass)
- 類表繼承歧沪。每個(gè)具體類一個(gè)表(table per concrete class)(有一些限制)
三種策略中的對(duì)象模型都是一樣的,如下:
二、一棵繼承樹映射成一張表(工程hibernate_extends_1
)
如:_animal
id | name | sex | weight | height | type |
---|---|---|---|---|---|
1 | 豬 | true | 100 | P | |
2 | 鳥 | false | 50 | B |
2.1、理解如何映射
因?yàn)轭惱^承樹肯定是對(duì)應(yīng)多個(gè)類竭望,要把多個(gè)類的信息存放在一張表中咬清,必須有某種機(jī)制來(lái)區(qū)分哪些記錄是屬于哪個(gè)類的奴潘。
-
這種機(jī)制就是萤彩,在表中添加一個(gè)字段(type),用這個(gè)字段的值來(lái)進(jìn)行區(qū)分。用hibernate實(shí)現(xiàn)這種策略的時(shí)候愚墓,有如下步驟:
- 父類用普通的
<class>
標(biāo)簽定義 - 在父類中定義一個(gè)discriminator昂勉,即指定這個(gè)區(qū)分的字段的名稱和類型
如:<discriminator column=”XXX” type=”string”/>
- 子類使用
<subclass>
標(biāo)簽定義岗照,在定義subclass的時(shí)候,需要注意如下幾點(diǎn):1.Subclass標(biāo)簽的name屬性是子類的全路徑名;
2.在Subclass標(biāo)簽中厚者,用discriminator-value
屬性來(lái)標(biāo)明本子類的discriminator字段(用來(lái)區(qū)分不同類的字段)的值Subclass標(biāo)簽迫吐,既可以被class標(biāo)簽所包含(這種包含關(guān)系正是表明了類之間的繼承關(guān)系)志膀,也可以與class標(biāo)簽平行。 當(dāng)subclass標(biāo)簽的定義與class標(biāo)簽平行的時(shí)候烫止,需要在subclass標(biāo)簽中馆蠕,添加extends
屬性,里面的值是父類的全路徑名稱吓妆。子類的其它屬性吨铸,像普通類一樣诞吱,定義在subclass標(biāo)簽的內(nèi)部。
- 父類用普通的
2.2沼瘫、理解如何存儲(chǔ)
存儲(chǔ)的時(shí)候hibernate會(huì)自動(dòng)將鑒別字段值插入到數(shù)據(jù)庫(kù)中咙俩,在加載數(shù)據(jù)的時(shí)候阿趁,hibernate能根據(jù)這個(gè)鑒別值正確的加載對(duì)象.
-
多態(tài)查詢:在hibernate加載數(shù)據(jù)的時(shí)候能鑒別出正真的類型(instanceOf)脖阵,請(qǐng)查看測(cè)試類中的各個(gè)方法。
- get支持多態(tài)查詢
- load只有在lazy=false呜呐,才支持多態(tài)查詢
- hql支持多態(tài)查詢
相關(guān)實(shí)體類:
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>
<!-- 定義一個(gè)type用來(lái)區(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>
說(shuō)明:這里會(huì)生成一張表蘑辑,即表_animal
以躯。注意type的類型是hibernate的string類型啄踊,不是java的String類型颠通。
測(cè)試:
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顿锰,通過(guò)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,通過(guò)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胳赌,通過(guò)Animal查詢
*/
public void testLoad3() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
Animal animal = (Animal)session.load(Animal.class, 1);
//因?yàn)閘oad默認(rèn)支持lazy匙隔,因?yàn)槲覀兛吹降氖茿nimal的代理對(duì)象
//所以通過(guò)instanceof是反應(yīng)不出正真的對(duì)象類型的
//因此load在默認(rèn)情況下是不支持多態(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纷责,通過(guò)Animal查詢,將<class>標(biāo)簽上的lazy=false
*/
public void testLoad4() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
Animal animal = (Animal)session.load(Animal.class, 1);
//可以正確的判斷出Pig的類型再膳,因?yàn)閘azy=false喂柒,返回的是具體的Pig類型
//此時(shí)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,通過(guò)Animal查詢
*/
public void testLoad5() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
//可以正確的判斷出Pig的類型湃番,因?yàn)榉祷氐氖蔷唧w的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,通過(guò)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);
}
}
}
說(shuō)明:
- 1.我們可以通過(guò)Animal查出相關(guān)的子類信息题禀,如方法
testLoad2
膀捷。 - 2.對(duì)于方法
testLoad3
,我們可以看到在lazy默認(rèn)情況下秀仲,我們是不能通過(guò)Animal反映出真正的對(duì)象類型壶笼。也就是load在默認(rèn)情況下是不支持多態(tài)查詢的。 - 3.采用load保礼,通過(guò)Animal查詢,將<class>標(biāo)簽上的lazy=false炮障。可以看到在方法
testLoad4
中可以正確判斷子類的類型企蹭。 - 4.在方法
testLoad5
中我們采用get方式通過(guò)Animal查詢徘键,是可以返回具體的子類類型的吹害。即get支持多態(tài)查詢。 - 5.在方法
testLoad6
中我們使用hql語(yǔ)句進(jìn)行查詢螺男。這里我們?cè)囼?yàn)了兩種類型的查詢纵穿,可以看到hql是支持多態(tài)查詢的谓媒,因?yàn)榉祷氐牟皇谴眍悾蔷唧w類土辩。
三抢野、每個(gè)子類映射成一張表(工程hibernate_extends_2
)
說(shuō)明:其中對(duì)象模型不變指孤,只是關(guān)系模型變了恃轩,于是我們需要重新配置關(guān)系模型,而我們的存儲(chǔ)加載都不需要改變补君,而其存儲(chǔ)加載的效率不如第一種挽铁。而第一種也有缺點(diǎn),里面有很多冗余字段楣铁,而且當(dāng)將冗余字段設(shè)置為非空更扁,那么是存儲(chǔ)不進(jìn)去的浓镜。一般情況下采用第一種。
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>
說(shuō)明:此時(shí)會(huì)在數(shù)據(jù)庫(kù)中生成三張表。測(cè)試的時(shí)候和上面例子是一樣的哄啄,這里不多說(shuō)咨跌。
四、每個(gè)具體類映射成一張表(工程hibernate_extends_3
)
說(shuō)明:每個(gè)具體類映射成一張表,對(duì)象模型還是沒(méi)有變化刊殉。如果使用uuid則相關(guān)的存儲(chǔ)加載不需要改變冗澈,但是如果使用assigned手動(dòng)分配則存儲(chǔ)加載需要做相應(yīng)的改動(dòng)陋葡,手動(dòng)設(shè)置主鍵腐缤。
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>
說(shuō)明:此時(shí)我們可以看到Animal就不會(huì)生成一張表了岭粤。同樣相關(guān)的測(cè)試都和上面例子一樣,這里不多說(shuō)巾兆。注意映射方式的不同角塑。
最后:比較來(lái)看,最后一種方式不能使用自增主鍵堤如。一般推薦使用第一種窒朋。同時(shí)我們最后一種方式中需要將animal表設(shè)置成abstract="true"
侥猩,這樣animal表就不會(huì)生成出來(lái),同時(shí)不會(huì)影響到存儲(chǔ)和加載洛退。