继承映射
对于面向对象的程序设计语言而言,继承和多态是两个最基本的概念。Hibernate的继承映射可以理解为持久化类之间的继承关系。
Hibernate支持三种继承映射的策略:
- subclass:将域模型中的每一个对象映射到一个独立的表中,也就是说不需要在关系型数据模型中考虑域模型的继承关系和多态。
- joined-subclass:对于继承关系中的子类使用同一个表,这需要在数据表中添加额外的属性区分子类类型的字段。
- union-subclass:域模型中的每个类映射到一个表中。通过关系数据模型中的外键来描述表之间的继承关系。相当于按照域模型中的结构建立数据库中的表,并通过外键建立表之间的继承关系。
使用subclass元素进行继承映射
使用subclass的继承映射可以实现继承关系中的父类和子类使用同一张表。
因为父类和子类保存在同一张表中,因此需要在表中添加额外的一列,使用该列来区分每行记录到底是哪个类的实例,这个列被称作辨别者列(discriminator)。
在这种映射策略下,使用subclass来映射子类,使用class或subclass的discriminator-value属性指定辨别者列的值。
所有子类定义的字段都不能有非空约束。如果为那些字段添加非空约束,那么父类实例在那些列中实际上并没有值,这将引起数据库完整性的冲突,导致父类实例无法保存到数据库中。
示例:
创建Person类(父类)
public class Person {
private Integer id;
private String name;
private int age;
//Getter and setter
}
创建Student类(子类)
public class Student extends Person {
private String school;
//Getter and setter
}
配置文件(Person.hbm.xml)
<hibernate-mapping package="com.hibernate.subclass">
<class name="Person" table="person" discriminator-value="PERSON">
<id name="id" type="java.lang.Integer">
<column name="ID"/>
<generator class="native"/>
</id>
<discriminator column="TYPE" type="java.lang.String"></discriminator>
<property name="name" type="java.lang.String">
<column name="NAME"/>
</property>
<subclass name="Student" discriminator-value="STUDENT">
<property name="school" type="java.lang.String" column="SCHOOL"></property>
</subclass>
</class>
</hibernate-mapping>
测试类
Person person = new Person();
person.setName("person1");
person.setAge(11);
session.save(person);
Student student = new Student();
student.setName("person2");
student.setAge(12);
student.setSchool("School1");
session.save(student);
对于子类对象只需要把记录插入到数据表中。辨别者列由Hibernate自动维护。
执行查询操作
List<Person>persons = session.createQuery("from Person").list();
System.out.println(persons.size());
查询父类记录只需要查询一张数据表。
对于子类记录,也只需要查询一张数据表。
缺点:
- 使用了辨别者列。
- 子类独有的字段不能添加非空约束。
- 如果继承的层次很深,会导致数据表的字段增多。
使用joined-subclass元素进行继承映射
采用joined-subclass元素继承的映射可以实现每个子类一张表。
采用这个映射策略时,父类实例保存在父类表中,子类实例由父类表和子类表共同存储,因为子类实例也是一种特殊的父类实例,因此也必然包括父类实例的属性,于是将子类和父类的共同属性保存在父类表中,子类增加属性,则保存在子类表中。
在这种映射策略下,无须使用辨别者列,但需要为每个子类使用key元素映射共有主键。
子类增加的属性可以添加非空约束。因为子类的属性和父类的属性没有保存在同一张表中。
示例
依然使用之前的类,只是Person.hbm.xml文件需要更改
<hibernate-mapping package="com.hibernate.joinedsubclass">
<class name="Person" table="person">
<id name="id" type="java.lang.Integer">
<column name="ID"/>
<generator class="native"/>
</id>
<discriminator column="TYPE" type="java.lang.String"></discriminator>
<property name="name" type="java.lang.String">
<column name="NAME"/>
</property>
<joined-subclass name="Student" table="student">
<key column="student_id"></key>
<property name="school" type="java.lang.String" column="SCHOOL"></property>
</joined-subclass>
</class>
</hibernate-mapping>
joined-subclass的优点:
- 不需要使用辨别者列。
- 子类的字段可以添加非空约束。
- 没有冗余的字段。
joined-subclass的缺点:
插入操作:
和subclass不同,joinedsubclass至少要插入到两张数据表中。
查询操作:
- 查询父类记录,做一个左外连接查询。
- 对于子类记录,做一个内连接查询。
采用union-subclass进行继承映射
采用union-subclass元素可以实现将每一个实体类对象映射到一个独立的表中。
子类增加的属性可以有非空约束,即父类实例的数据保存在父表中,而子类实例的数据保存在子类表中。
子类实例的数据仅保存在子类表中,而在父类表中没有任何记录。
在这种映射策略下,子类表的字段会比父类表的字段要多,因为子类表的字段等于父类表的字段,加子类表新增的字段的总和。
在这种映射策略下,既不需要使用辨别者列,也不使用key元素映射共同的主键。
使用union-subclass的映射策略是无法使用identity的主键生成策略。因为同一类继承层次中所有实体类都要使用同一个主键种子。即多个持久化实体对应的记录的主键应该是连续的,受此影响,也不应该使用native的主键生成策略。因为native会根据数据库使用identity或者sequence。
同样只需要修改Person.hbm.xml即可
<hibernate-mapping package="com.hibernate.unionsubclass">
<class name="Person" table="person">
<id name="id" type="java.lang.Integer">
<column name="ID"/>
<generator class="native"/>
</id>
<property name="name" type="java.lang.String">
<column name="NAME"/>
</property>
<union-subclass name="Student" table="student">
<property name="school" type="java.lang.String" column="SCHOOL"></property>
</union-subclass>
</class>
</hibernate-mapping>
union-subclass的优点:
- 无需使用辨别者列。
- 子类的字段可以添加非空约束。
- 对于子查询只需要查询一张表。
union-subclass的缺点:
- 存在冗余的字段。
- 查询父类的性能偏弱,因为需要有子查询,将父表和子表的记录汇聚到一起。
- 若更新父表的字段,则更新的效率较低。