继承关系映射
继承是面向对象编程中一个很重要的特征,在做面向对象的分析与设计时,经常会设计出具体继承关联映射的持久化类。如何把这些持久化类之间的继承关系映射到关系数据库上的问题已解决。继承在对象模型中是is a的关系,但在关系模型中,实体之间只有has a的关系,也就是说,继承在对象模型和关系模型上是不匹配的。所幸的是,Hibernate提供了3种常用的继承映射方案。
举例讲解这3种继承方案的使用:笔者在开发一个音乐网站时就涉及继承映射的问题,音乐网站中的歌手可分为单人歌手、乐队组合等。
1) Singer类
/**歌手抽象类*/
public abstract class Singer{
private Long id ;//对象标示符
private String name;//名称
private String region;//歌手所属区域(如:华人、日韩、欧美等)
private String description;//描述
public Singer(){}//无参构造方法
//以下为省略的所有的getter()与setter()方法
}
SingleSinger类
/**单人歌手实体类*/
public calss SingleSinger extends Singer{
private Character gender;//性别
private SingleSinger(){}//无参构造方法
//以下省略为所有属性的getter()和setter()方法
}
Bands类
/**乐队组合实体类*/
public class Bands extends Singer{
private String leader;//主唱姓名
public Bands(){}//无参构造方法
//以下为所有属性的getter()和setter()方法
}
1. 整个继承层次一张表
把整个继承层次的多个类的信息存放到一张表里。需要在表中添加一个特定的字段,用这个字段的值来进行区分哪些记录是属于哪个类的。
对应的映射文件Singer.hbm.xml
<?xml version=”1.0” encoding=”UTF-8”?>
<!DOCTYPE hibernate-mapping PUBLIC
“-//Hibernate/Hibernate Mapping DTD 3.0//EN”
http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd>
<hibernate-mapping>
<!-- 映射持久化类 -- >
<class name=”com.qiujy.domain.Singer” table=”singer”>
<! -- 映射对象标示符 -- >
<id name=”id” column=”id” type=”long”><generator class=”native”/></id>
<!-- 指定鉴别器字段,要放置在所有属性映射之前 -- >
<discriminator column=”type” type=”string”/>
<!-- 映射普通属性 -- >
<property name=”name”/>
<property name=”region”/>
<property name=:”description”/>
<!-- 用subclass映射子类,指定鉴别字段值 -- >
<subclass name=”com.qiujy.domain.SingleSinger” discriminator value=”S”>
<!-- 映射本子类的属性 -- >
<property name=”gender”>
</subclass>
<!-- 用subclass映射子类,指定鉴别字段值 -- >
<subclass name=”com.qiujy.domain.Bands” discriminator-value=”B”>
<!-- 映射本子类的属性 -- >
<property name=”leader”/>
</subclass>
</class>
</hibernate-mapping>
这个映射方案在性能和简单性方面很好,在多态和非多态的查询上也表现的很好,是继承映射中使用比较广泛的方案。但这个映射文件也由它的缺点:子类中属性的值必须是可以为空的;这种映射方案的关系模型表设计也违背了第三范式。
2. 每个子类一张表
这种方案是把对象模型上的继承关系表示为关系模型中的外键关联,继承结构中的每个类和子类都有一张对应的数据库表。
对应的映射文件Singer.hbm.xml内容如下:
<?xml version=”1.0” encoding=”UTF-8”?>
<DOCTYPE hibernate-mapping PUBLC
“-//Hibernate/Hibernate Mapping DTD 3.0//EN”
http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd
<hibernate-mapping>
<!-- 映射持久化类 -- >
<class name=”com.qiujy.domain.Singer”table=”singer”>
<!-- 对象标示符 -- >
<id name=”id” column=”id” type=”long”><generator class=”native”/></id>
<!-- 映射普通属性 -- >
<property name=”name”/>
<property name=”region”/>
<property name=”description”/>
<!-- 用jioned-subclass元素给每个子类映射到一张表-- >
<joined-subclass name=”com.qiujy.domain.SingleSinger” table=”single_singer”>
<!-- 用key元素来指定子类和父类之间是通过哪个字段来关联的 -- >
<key column=”singer_id”/>
<property name=”gender”/>
</joined-subclass>
<!-- 用jioned-subclass 元素给每个子类映射到一张表 -- >
<joined-subclass name=”com.qiujy.domain.Bands” table=”bands”>
<!-- 用key 元素来指定子类和父类之间是通过哪个字段来关联的 -- >
<key column=”bands_id”/>
<!-- 映射本子类的属性 -- >
<property name=”leader”/>
</joined-subclass>
</class>
</hibernate-mapping>
这种映射方案:在父类对应的数据库表中,实际上会存储所有的记录,包括父类和子类的记录;在子类对应的数据库表中,这个表只存储子类中所特有的属性映射的字段值;子类与父类,通过相同的主键值来关联。
3. 每个具体类一张表
这种策略是针对每个具体类对应一张表,而且这个表的信息是完备的,它从包含所有从父类继承下来的属性映射的字段和自己的属性应的字段。
相应的映射文件Singer.hbm.xml如下:
<?xml version=”1.0” encoding=”UTF-8”?>.
<DOCTYPE hibernate-mapping PUBLIC
“-//Hibernate/Hibernate Mapping DTD 3.0//EN”
http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd>
<hibernate-mapping>
<!-- 指定此类为抽象类,不需要映射到对应的表 -- >
<class name=”com.qiujy.comainn.Singer” abstract=”true”>
<!-- 映射对象标示符 -- >
<id name=”id” column=”id” type=”long”>
<!-- 映射主键生成方式为由java应用程序负责生成标示符 -- >
<generator class=”assigined”/>
</id>
<!-- 映射普通属性 -- >
<property name=”name”/>
<prpperty name=”region”/>
<property name=”description”/>
<!-- 用union-subclass 元素给每个具体子类映射到一张表 -- >
<union-subclass name=”com.qiujy.domain.SingleSinger” table=”single-singer”>
<!-- 映射本子类的属性 -- >
<property name=”gender”/>
</union-subclass>
<!-- 用union-subclass元素给每个具体子类映射到一张表 -- >
<union-subclass name=”com.qiujy.domain.Bands” table=”bands”>
<!-- 映射本子类的属性 -- >
<property name=”leader”/>
</class>
</hibernate-mapping>
继承映射最常用的方案就是以上介绍的三种,在实际开发中,怎么选择合适的方案。笔者根据自己的工作经验,给读者提供一些经验。
如果不需要多态查询:使用每个具体类一张表
一定要使用多态查询:子类中的属性相对较少,使用每个继承层次一张表
子类中的属性较多,使用每个子类一张表
简单的问题一般选择每个继承层次一张表,复杂案例一般选择每个子类一张表