初识Hibernate之继承映射

     前面的两篇文章中,我们介绍了两张表之间的各种相互关联映射关系,但往往我们也会遇到两张表甚至多张表之间共有着多个相同的字段。例如:

这里写图片描述

如图,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或者其他共有字段的信息,只需要通过自己的主键去查找父表即可得到。

至此,有关继承映射的三个策略已经介绍结束,总结不到之处,望指出。

posted @ 2017-10-16 16:55  Single_Yam  阅读(405)  评论(0编辑  收藏  举报