Hibernate关于继承的加工
普通的JDBC只能返回数据库中的Table(ResultSet),难以返回面向中继承体系中的子类。本文就来介绍如何通过Hibernate的加工,让OO Design得以美梦成真。 E/R Model在结构上多半属于”has a“的关系,无法表示”is a“的继承关系。这里Hibernate会进行加工,屏蔽关系数据库和面向对象的本质差异。
继承的OR-Mapping方式不止一种,下面逐一介绍。
1.Table per class hierarhcy(这个继承层次就一张表)
对象的关系为:父类为Singer,有两个子类,分别是SingleSinger和BandsSinger。
public class Singer { private long id; private String name; private String region; private String description; // setter、getter方法 } class SingleSinger extends Singer{ private String gender;
// setter、getter方法 } class BandsSinger extends Singer{ private String leader;
// setter、getter方法
}
这种方式既E-R Diagram中只有一个table。既多个类的信息存放到一个表中。需要在表中添加一个特定字段,用这个字段的值来进行区分哪些纪录是属于那个类的。如下图所示
对应的Singer.hbm.xml配置文件如下
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="com.bupt.domain"> <class name="Singer" table="singer"> <id name="id" column="id" type="long"> <generator class="native" /> </id> <!-- 指定鉴别器字段,要放于所有属性映射之前,id之后的位置 --> <discriminator column="type" type="string"></discriminator> <property name="name" column="name" /> <property name="region" column="region"></property> <property name="description" column="description"></property> <!-- 用subclass用来映射子类 --> <subclass name="SingerSinger" discriminator-value="S"> <property name="gender" column="gender"></property> </subclass> <subclass name="BandsSinger" discriminator-value="B"> <property name="leader" column="leader"></property> </subclass> </class> </hibernate-mapping>
Tips:这个映射方案在性能方面很好,也很简单,在多态查询方面表现的也很好。是使用较为广泛的方案。但是其缺点是:1.子类的属性值不能有非空(NOT NULL)
约束。2.关系模型表的设计违背了第三范式。
2.Talbe per subclass 每个子类一张表
E-R Diagram有三个Table,如下图所示。类之间的继承关系表示为关系模型中的外键关联。继承结构中的每个类和子类(包括接口和抽象类)都有一张对应的数据库表。
对应的映射文件为
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="com.bupt.domain"> <class name="Singer" table="singer"> <id name="id" column="id" type="long"> <generator class="native" /> </id> <property name="name" column="name" /> <property name="region" column="region"></property> <property name="description" column="description"></property> <!-- 用joined-subclass定义子类 --> <joined-subclass name="SingleSinger" table="single_singer"> <!-- 用key指定子类和父类通过那个字段相连 --> <key column="single_id" ></key> <!-- 使用property映射本类 属性 --> <property name="gender" column="gender"></property> </joined-subclass>
<joined-subclass name="BandsSinger" table="bands_singer"> <!-- 用key指定子类和父类通过那个字段相连 --> <key column="bands_id" ></key> <!-- 使用property映射本类 属性 --> <property name="leader" column="leader"></property> </joined-subclass> </class> </hibernate-mapping>
Tips:需要三张表。两个子类表通过主键关联到超类表。查询时候的Join非常多,即使是新增、删除、修改,也会涉及两个以上的Table。这种方法并不适合复杂的分层体系。
3.Table per concrete class with unions(每个具体类对应一张表)
每个具体类对应一个张表,而且这个表的信息是完备的。它包括所有从父类继承下来的属性映射的字段和自己的属性映射字段。
对应的配置文件是
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="com.bupt.domain"> <class name="Singer" table="singer"> <!-- 主键的生成策略,为所有的子类所共享 --> <id name="id" column="id" type="long" unsaved-value="null"> <generator class="native" /> </id> <property name="name" column="name" /> <property name="region" column="region"></property> <property name="description" column="description"></property> <!-- 用union-subclass定义子类 --> <union-subclass name="SingleSinger" table="single_singer"> <!-- 使用property映射本类 属性 --> <property name="name" column="name" /> <property name="region" column="region"></property> <property name="description" column="description"></property> <property name="gender" column="gender"></property> </union-subclass> <union-subclass name="BandsSinger" table="bands_singer"> <!-- 使用property映射本类 属性 --> <property name="name" column="name" /> <property name="region" column="region"></property> <property name="description" column="description"></property> <property name="leader" column="leader"></property> </union-subclass> </class> </hibernate-mapping>
Tips:这里涉及两张与子类相关的表。每张表为对应类的所有属性(包括从超类继承的属性)定义相应字段。
这种方式的局限在于,如果一个属性在超类中做了映射,其字段名必须与所有子类 表中定义的相同。(我们可能会在Hibernate的后续发布版本中放宽此限制。) 不允许在联合子类(union subclass)的继承层次中使用标识生成器策略(identity generator strategy), 实际上, 主键的种子(primary key seed)不得不为同一继承层次中的全部被联合子类所共用.假若超类是抽象类,请使用abstract="true"
。当然,假若它不是抽象的,需要一个额外的表,来保存超类的实例。
最后,请注意,任何一种OR-Mapping方式,其实体对象的写法都是一样的。这也验证了,通过hibernate的封装效果。使得Table结构的改变,也不至于影响程序代码。
使用哪一种方式,需要根据情况而定:
- table-per-class-hierarchy对于non-null的限制
- 空间的考虑
- 多态查询的考虑
待续。。。。