【前言】
根据图建立对应的对象模型,也就是所说的实体类,本篇博客中实体类代码就不再展示了。
3. 配置及注释:
不知道大家是否还记得UML中的四种关系?自己回想了一下,还是没有忘记的,分别是继承、实现、依赖和关联。
怎么突然会想到这样一个问题?是因为在学习完Hibernate关联映射之后,紧接着又来了一个继承映射。关联和继承,都属于四种关系之一,所以,本篇博客就先提了个问题,下面就开始继承映射的学习。
【概述】
继承是面向对象语言的三大重要特性之一,它实现了代码的复用。而Hibernate对于此特性在对应的对象模型配置文件中也作出了各种配置,可将其与关系模型的关系分为三种情况:
1. 每棵类继承树一张表
2. 每个类一张表
3. 每个具体类一张表
下面我们就逐一看看,它们各自生成的关系模型有着哪些不同?以下通过动物类和猪类、鸟类三者间的关系为实例,利用不同继承映射的方法去建表。
三个类之间的关系如下所示:
【一、继承映射之每棵类继承树一张表】
1. 含义:此方法的意思是不管父类拥有多少个子类,都只会根据父类去生成一张表,而各个子类中有单独的字段,都添加到父类生成的那张表中。
2. 设计:
从图上看,这张表至少包含的字段有:id,name,sex,weight和height。但因为子类不止一个,如果没有一个标识字段,那么就无法从数据表中看到记录是属于哪个子类的。所以,此张表的结构与数据应该如下:
t_animal:
Id | Name | Sex | Weight | Height | Type |
1 | 小猪猪 | True | 200 | P | |
2 | 小鸟鸟 | False | 100 | B |
3. 配置及注释:
<?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="com.bjpowernode.hibernate">
<!-- 新建表,名为t_animal。不采用延迟加载 -->
<class name="Animal" table="t_animal" lazy="false">
<!-- 该表的主键字段id,生成策略为native -->
<id name="id">
<generator class="native"/>
</id>
<!-- 在父类使用discrimination标签,用来指定标识字段的列名和类型 -->
<discriminator column="type" type="string"/>
<!-- 父类的name属性 -->
<property name="name"/>
<!-- 父类的sex属性 -->
<property name="sex"/>
<!-- 子类Pig,标识字段值为P -->
<subclass name="Pig" discriminator-value="P">
<!-- 子类Pig的weight属性 -->
<property name="weight"/>
</subclass>
<!-- 子类Bird,标识字段值为B -->
<subclass name="Bird" discriminator-value="B">
<!-- 子类Bird的height属性 -->
<property name="height"/>
</subclass>
</class>
</hibernate-mapping>
4. 结果:
5. 小结:
此种方法的结果就是数据库中只有一张根据父类建立的表,而不同子类的子属性也全都写入到这张表中,增加一个区分字段即可。
6. 优缺点:
(1)表中引入的区分子类的字段,也就是包括了描述其他字段的字段。
(2)如果某个子类的某个属性不能为空,那么在数据库一级不能设置该字段not null(非空)
(3)维护起来方便,只需要修改一个表
(4)灵活性差,表中冗余字段会随着子类的增多而越来越多
【二、继承映射之每个类一张表】
1. 含义:此种方法的意思是每个类都建立一张单独的表。
2. 设计:从图上看,共包含三个类,则应该建立三张表:t_animal、t_pig和t_bird。三张表的结果和数据如下所示:
3. 配置即注释:
<?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="com.bjpowernode.hibernate">
<!-- 建立表t-animal -->
<class name="Animal" table="t_animal">
<!-- 该表的主键字段id,生成策略为native -->
<id name="id">
<generator class="native"/>
</id>
<!-- t_animal的属性字段name和sex -->
<property name="name"/>
<property name="sex"/>
<!-- joined-subclass生成策略,新建表t_pig -->
<joined-subclass name="Pig" table="t_pig">
<!-- 指定了pig子类和animal父类之间是通过pid字段关联 -->
<key column="pid"/>
<!-- pig子类的子属性,weight -->
<property name="weight"/>
</joined-subclass>
<!-- joined-subclass生成策略,新建表t_bird -->
<joined-subclass name="Bird" table="t_bird">
<!-- 指定了bird子类和animal父类之间是通过bid字段关联 -->
<key column="bid"/>
<!-- bird子类的子属性,height -->
<property name="height"/>
</joined-subclass>
</class>
</hibernate-mapping>
4. 结果:
5. 小结:
此种方法的结果是数据库中每个类都对应生成一张表,父类拥有自己的属性字段,子类加上与父类关联的主键字段和自己的子属性。
6. 优缺点:
(1)这种设计方式完全符合关系模型的设计原则,且不存在冗余
(2)维护起来比较方便,对每个类的修改只需要修改其所对应的表,灵活性很好,完全是参照对象继承的方式进行配置
(3)对于父类的查询需要使用左外链接,对于子类查询需要使用内链接
(4)对于子类的持久话至少要处理两个表
【三、继承映射之每个具体类一张表】
1. 含义:此种方法的意思是每个子类都建立一张单独的表,而父类不生成对应的表。
2. 设计:从图上看,共包含两个具体类,则应该建立两张表:t_pig和t_bird。两张表的结果和数据如下所示:
3. 配置即注释:
<?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="com.bjpowernode.hibernate">
<!-- 定义该表的abstract属性为true,则不会生成具体的表 -->
<class name="Animal" table="t_animal" abstract="true">
<id name="id">
<!-- 让应用程序在save()之前为对象分配一个标示符。这是 <generator>元素没有指定时的默认生成策略。 -->
<generator class="assigned"/>
</id>
<property name="name"/>
<property name="sex"/>
<!-- union-subclass生成策略,t_pig表的信息是完整的,也就是包含id,name,sex和weight四个字段 -->
<union-subclass name="Pig" table="t_pig">
<!-- 子属性字段weight -->
<property name="weight"/>
</union-subclass>
<!-- union-subclass生成策略,t_bird表的信息是完整的,也就是包含id,name,sex和height四个字段 -->
<union-subclass name="Bird" table="t_bird">
<!-- 子属性字段height -->
<property name="height"/>
</union-subclass>
</class>
</hibernate-mapping>
4. 结果:
5. 小结:
此种方法的结果是数据库中每个子类都对应生成一张表,每个子类的属性是各自的子属性加上父类的共同属性。
6. 优缺点:
(1) 这种设计方式符合关系模型的设计原则,但有表中存在重复字段的问题。
(2) 如果需要对基类进行修改,则需要对基类以及该类的子类所对应的所有表都进行修改
(3) 对于子类的查询只需要访问单独的表,对父类查询需要检索所有的表,对于单个对象持久话操作只需要处理一个表
【总结】
对于这三种继承映射方法,目前还没有在项目中遇到过,所以三者之间如何选择?通常选择什么?都没有概念。不过总体思想应该仍旧是:没有最好,选择合适的就好。也期待在项目中见到对它们的应用。