前面的两篇文章中,我们介绍了两张表之间的各种相互关联映射关系,但往往我们也会遇到两张表甚至多张表之间共有着多个相同的字段。例如:
如图,student表和teacher表共同具有id,name,age等字段,而我们的继承映射就是这样的一个思想,抽象出这些共有的字段为一张父表,让子表对其进行继承,这样就可以大大降低我们实体类代码的冗余性,增强其结构的完善。
Hibernate支持以下三种数据表的生成策略:
- 每个类分层结构一张表
- 每个子类一张表
- 每个具体类一张表
一、每个类分层结构一张表
对于这种数据表的生成策略,最终Hibernate会在数据库中生成一张数据表,这张数据表的结构会是如下的:
无论你是student记录还是teacher记录,都会被存储到这张集成的新表中。下面是实现代码:
public class Person {
private int id;
private String name;
//省略get,set方法
}
public class Student extends Person {
private int grade;
//省略get,set方法
}
public class Teacher extends Person {
private int salary;
//省略get,set方法
}
下面是映射配置文件的书写:
<class name="Person" abstract="true" table="newTable">
<id name="id">
<generator class="assigned"/>
</id>
<!--指定鉴别器列的名称和类型-->
<discriminator column="type" type="java.lang.String"/>
<property name="name"/>
<!--指定集成到新表的子实体类-->
<subclass name="Student" discriminator-value="stu">
<!--配置继承类中其他属性-->
<property name="grade"/>
</subclass>
<subclass name="Teacher" discriminator-value="tea">
<property name="salary"/>
</subclass>
</class>
abstract="true"指定Hibernate不用将Person表生成具体表,只需要生成一张集成的新表即可。table="newTable"则指定了新表的表名,鉴别器就是用来区分当前记录时student或是teacher的一个标记,它会在新表中生成一个字段,而该字段的名称和类型都在此处进行指定。
subclass标签用于配置需要集成到新表中子实体类的一些属性等内容。下面我们通过插入操作了解这种映射策略对具体数据库的实际操作。
Student student = new Student();
student.setId(1);
student.setName("stu1");
student.setGrade(100);
Teacher teacher = new Teacher();
teacher.setId(2);
teacher.setName("tea1");
teacher.setSalary(10000);
session.save(student);
session.save(teacher);
将两条不同类型的记录保存到数据库中,
newTable是一张综合的表,主要由student表和teacher表集成而来,对应于student表的记录salary字段的值为空,对应于teacher表记录的grade字段值为空。那在我们取数据的时候,Hibernate该如何区分当前记录对应的是哪个实体类的呢?
Person person = (Person)session.get(Person.class,1);
if(person instanceof Student){
Student student1 = (Student)person;
System.out.println("i am student,my grade is:"+student1.getGrade());
}
实际上,Hibernate使用多态来对数据记录进行接收,无论你是Student记录或teacher记录,都可以被person类型接收。使用这种策略方式映射数据表只会生成一张表,但是很大的一个问题是,当很多张表进行集成的时候会导致表结构复杂混乱。
二、每个具体类映射成一张表
这种数据表的生成策略会为每个实体类生成一张数据表,就上述例子而言,会为person,student,teacher都生成一张表。首先我们看映射配置文件:
<class name="Person">
<id name="id">
<generator class="assigned"/>
</id>
<property name="name"/>
<union-subclass name="Student" table="students">
<property name="grade"/>
</union-subclass>
<union-subclass name="Teacher" table="teachers">
<property name="salary"/>
</union-subclass>
</class>
这里的配置其实不需要多的解释,union-subclass告诉Hibernate当前配置的实体类是person类的子类,并用table属性指定对应数据库的表名。下面我们通过插入数据来看看具体生成的表的结构:
很明显,我们的person表只起到一个模板的效果并没有什么实际的价值,而我们students表和teachers表的id,name字段都是从person表中继承得到的。
三、每个子类一张表
这种数据表的生成策略的主要思想就是将公共的信息存放在父表中,子表只保存自己独有的字段信息了。例如:
<class name="Person">
<id name="id" column="id">
<generator class="native"/>
</id>
<property name="name"/>
<joined-subclass name="Student" table="students">
<key column="sId" />
<property name="grade"/>
</joined-subclass>
<joined-subclass name="Teacher" table="teachers">
<key column="tId"/>
<property name="salary"/>
</joined-subclass>
</class>
我们使用 joined-subclass来配置继承子类,在Hibernate生成数据表的时候会将student和teacher表的主键作为外键关联父表的主键。下面是插入数据:
Student student = new Student();
student.setName("stu1");
student.setGrade(12);
Teacher teacher = new Teacher();
teacher.setName("tea1");
teacher.setSalary(1233);
session.save(student);
session.save(teacher);
显然,我们student或者teacher表不再需要存放name或者其他共有字段的信息,只需要通过自己的主键去查找父表即可得到。
至此,有关继承映射的三个策略已经介绍结束,总结不到之处,望指出。