继承、泛化、关联是面向对象常见关系,在实际编程中,不少出现它们的身影。Hibernate作为JDBC的良好包装者,对于这些常见关系也通过配置文件得以灵活实现。
在Hibernate应用中,实体间继承关系的应用主要有三种策略:
1、单表继承:n个继承实体共用1张表
2、具体表继承:每个子类分别创建1张表
3、类表继承:每个具体类分别创建一张表
其中以1、2方式使用居多。下面先来看看三个策略的具体使用,通过对比,总结相互的异同点。
一、单表继承:
小猪、鸟继承与动物类:
1、创建3个实体类 (此处省略get set方法),
public class Animal {
//公有属性
private int id;
private String name;
private String sex;
}
public class Bird extends Animal{
private int height;
}
public class Pig extends Animal{
private int weight;
}
2、编写配置文件,只创建一张t_animal表,加入鉴别字段,判断动物类型:
<hibernate-mapping package="com.bjpowernode.hibernate">
<class name="Animal" table="t_animal">
<id name="id">
<generator class="native"/>
</id>
<!-- 加入鉴别字段 type 猪p 鸟b -->
<discriminator column="type" type="string" ></discriminator>
<property name="name"/>
<property name="sex"/>
<subclass name="Pig" discriminator-value="P">
<property name="weight"></property>
</subclass>
<subclass name="Bird" discriminator-value="B">
<property name="height"></property>
</subclass>
</class>
3、导出表结构,CRUD测试结果(此处省略部分代码)
//插入:
Pig pig=new Pig();
pig.setName("little");
pig.setSex("male");
pig.setWeight(130);
//注意将对象纳入session管理
session.save(pig);
Bird bird=new Bird();
bird.setName("bird");
bird.setSex("famale");
bird.setHeight(178);
//注意将对象纳入session管理
session.save(bird);
//加载
Pig pig=(Pig)session.load(Pig.class, 1);
System.out.println(pig.getName());
以上两个测试方法,结果分别为 ,
t_animal表中分别新增pig 和bird两条记录,加载显示pig名称
通过例子可以看出,单表继承的特点就是不管继承关系存在多少类,配置文件就1个,创建的表就1张。它通过加入鉴别字段,判断子类类型,插入数据库。这样做的好处就在于表单一,好维护,crud时不用关联过多表获取数据。但缺点就在于数据比较冗余。如上,鸟没有weight属性,则该字段在数据库中为空,同理,小猪也没有height属性,则该字段在数据库中也为空。
二、具体表继承:
同样继承关系,我们来看看具体表继承是如何创建表的。
1、3个实体类同上
2、编写配置文件:
<hibernate-mapping package="com.bjpowernode.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="t_pig">
<key column="pid"></key>
<property name="weight"></property>
</joined-subclass>
<joined-subclass name="Bird" table="t_bird">
<key column="bid"></key>
<property name="height"></property>
</joined-subclass>
</class>
</hibernate-mapping>
3、导出表结构,CRUD测试结果
导出的表结构如下:
测试类不变,同样可生成数据,不过是t_pig,t_bird,t_animal中分别生成1,,1,2条数据。
从创建的表关系可以看出,具体表继承是在继承关系中有多少个类,创建多少张表。父表包含多个从表公有属性字段,从表中通过逐渐id与父表id关联维护关系。这种策略就避免了单表继承数据冗余的缺点。但就是表相对较多,查询时可能需要关联多张表操作,操作数据比较繁琐。
三、类表继承:
1、3个实体类同上
2、编写配置文件:
<class name="Animal" table="t_animal">
<id name="id">
<generator class="assigned"/>
</id>
<discriminator column="type" type="string"/>
<property name="name"/>
<property name="sex"/>
<union-subclass name="Pig" table="t_pig">
<!-- 木有key外键了<key column="pid"></key> -->
<property name="weight"></property>
</union-subclass>
<union-subclass name="Bird" table="t_bird">
<!-- <key column="bid"></key> -->
<property name="height"></property>
</union-subclass>
</class>
3、导出表结构,CRUD测试结果
同样三张表,但插入数据时,只对子表也就是t_pig,t_bird插入,而t_animal并未改动数据。
所以在这个策略中,利用抽象-具体的概念,某个子类是具体类,创建相应表。而父类抽象,t_animal 表不负责实际存值,只是一张抽象的表。笔者目前还没发现这张抽象表起何作用,并且可以通过abstract=“true”,将其省略。第三种方式,虽然也类似于子类各一张表,但个表中数据是完整的,父类虽然包含了公有属性,但对应父表并不起数据存储作用,每个公有属性都分别由子表自己存储。对于继承关系,笔者认为并未起到太大作用,只是配置文件用了<union-subclass>标签关联。