一、继承映射的三种策略:
- 单表继承。一棵类继承树使用一个表(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、理解如何映射
因为类继承树肯定是对应多个类,要把多个类的信息存放在一张表中,必须有某种机制来区分哪些记录是属于哪个类的。
-
这种机制就是,在表中添加一个字段(type),用这个字段的值来进行区分。用hibernate实现这种策略的时候,有如下步骤:
- 父类用普通的
标签定义 - 在父类中定义一个discriminator,即指定这个区分的字段的名称和类型
如: - 子类使用
标签定义,在定义subclass的时候,需要注意如下几点:1.Subclass标签的name属性是子类的全路径名;
2.在Subclass标签中,用discriminator-value
属性来标明本子类的discriminator字段(用来区分不同类的字段)的值Subclass标签,既可以被class标签所包含(这种包含关系正是表明了类之间的继承关系),也可以与class标签平行。 当subclass标签的定义与class标签平行的时候,需要在subclass标签中,添加extends
属性,里面的值是父类的全路径名称。子类的其它属性,像普通类一样,定义在subclass标签的内部。
- 父类用普通的
2.2、理解如何存储
存储的时候hibernate会自动将鉴别字段值插入到数据库中,在加载数据的时候,hibernate能根据这个鉴别值正确的加载对象.
-
多态查询:在hibernate加载数据的时候能鉴别出正真的类型(instanceOf),请查看测试类中的各个方法。
- get支持多态查询
- load只有在lazy=false,才支持多态查询
- hql支持多态查询
相关实体类:
Animal.java
private int id;
private String name;
private boolean sex;
Pig.java
private int weight;
Bird.java
private int height;
配置:
extends.java
说明:这里会生成一张表,即表_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在默认情况下是不支持多态查询的
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查询,将标签上的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支持多态查询
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支持多态查询
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是支持多态查询的
// 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在默认情况下是不支持多态查询的。 - 3.采用load,通过Animal查询,将
标签上的lazy=false。可以看到在方法 testLoad4
中可以正确判断子类的类型。 - 4.在方法
testLoad5
中我们采用get方式通过Animal查询,是可以返回具体的子类类型的。即get支持多态查询。 - 5.在方法
testLoad6
中我们使用hql语句进行查询。这里我们试验了两种类型的查询,可以看到hql是支持多态查询的,因为返回的不是代理类,而是具体类。
三、每个子类映射成一张表(工程hibernate_extends_2
)
说明:其中对象模型不变,只是关系模型变了,于是我们需要重新配置关系模型,而我们的存储加载都不需要改变,而其存储加载的效率不如第一种。而第一种也有缺点,里面有很多冗余字段,而且当将冗余字段设置为非空,那么是存储不进去的。一般情况下采用第一种。
extends.hbm.xml
说明:此时会在数据库中生成三张表。测试的时候和上面例子是一样的,这里不多说。
四、每个具体类映射成一张表(工程hibernate_extends_3
)
说明:每个具体类映射成一张表,对象模型还是没有变化。如果使用uuid则相关的存储加载不需要改变,但是如果使用assigned手动分配则存储加载需要做相应的改动,手动设置主键。
extends.hbm.xml
说明:此时我们可以看到Animal就不会生成一张表了。同样相关的测试都和上面例子一样,这里不多说。注意映射方式的不同。
最后:比较来看,最后一种方式不能使用自增主键。一般推荐使用第一种。同时我们最后一种方式中需要将animal表设置成abstract="true"
,这样animal表就不会生成出来,同时不会影响到存储和加载。