Hibernate学习_016_继承映射

在JAVA面向对象的世界里,继承关系描述了对象之间的所属关系,Hibernate对于java的继承关系也有一定的支持。

在Hibernate中实现继承映射,有三种方式:

第一种:SINGLE_TABLE。(最常用的一种方式

这种方式,将所有子类的字段和父类字段全部放到一个表中,然后用一个区分字段(Discriminator value)来区分一条记录倒地属于那个类。

其优点是:三种方式中最简单的一种,所有字段都在一个表中,查询的时候,不用做表之间的关联查询,速度最快,查询效率最高。

其缺点是:1:子类所属的各个字段不可以加NOT NULL限制。2:如果一个子类存在大量数据字段,那一条记录中,将会产生大量的Null字段,而且表结构会很大,所以在子类比较少,而且子类的字段数不是特别多的时候才考虑使用这种映射方式。

代码示例:(注:本文所讲三种映射,均已Person、Teacher、Student这三个类为例,其中Teacher、Student均是Person的子类)

Person超类:(在这个继承树的超类中,我们指定映射方式为SINGLE_TABLE,同时指明 数据表 对应的区分字段名叫“discriminator”,超类Person对应的此字段值叫做“person”)

@Entity
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="discriminator",discriminatorType=DiscriminatorType.STRING)
@DiscriminatorValue(value="person")
public class Person{
	private int id;
	private String name;
	@Id
	@GeneratedValue
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
}
子类Teacher:(子类Teacher只需要指明此子类在表中的区分字段是“teacher”即可)

@Entity
@DiscriminatorValue("teacher")
public class Teacher extends Person{
	private String title;
	public String getTitle() {
		return title;
	}
	public void setTitle(String title) {
		this.title = title;
	}
}
子类Student:(子类Student只需要指明此子类在表中的区分字段是“student”即可)

@Entity
@DiscriminatorValue("student")
public class Student extends Person {
	private int score;
	public int getScore() {
		return score;
	}
	public void setScore(int score) {
		this.score = score;
	}
}
运行测试代码,查看生成的表结构,我们得到如下结果:
16:29:04,084 DEBUG SchemaExport:377 - 
    create table Person (
        discriminator varchar(31) not null,
        id integer not null auto_increment,
        name varchar(255),
        score integer,
        title varchar(255),
        primary key (id)
    )

第二种:TABLE_PER_CLASS

这种方式,就是每个类对应一张表。不论是超类还是子类,而且特别说明的是,各个子类表中存放从超类那里继承而来字段,也就是各个子类所对应的表的字段是完备的。

其优点是:层次分明、结构清晰

其缺点是:效率不高,存在表之间的关联操作,而且,要有一个存放各个子类表的主键取值的一个中间表(主键种子表)。各个子表中的主键值依次参照这个主键种子取值,所以,各个子表中的主键一般不会是连续的。

代码示例:

Person:

@Entity
@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
@TableGenerator(
		name="t_gen",
		table="t_gen_table",
		pkColumnName="pk",
		valueColumnName="pk_value",
		initialValue=1,
		allocationSize=1,
		pkColumnValue="now_key"
		)
public class Person{
	private int id;
	private String name;
	@Id
	@GeneratedValue(generator="t_gen",strategy=GenerationType.TABLE)
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
}
在这个超类中,我们指点了关联各个子表主键的主键种子表是t_gen_table,并且定义了此表的其他的详细信息。在ID生成策略中我们通过strategy=GenerationType.TABLE来指定ID的生成策略。

Teacher、Student这两个子类,我们就不在需要加入@DiscriminatorValue("XXXX")了,只需要加入@Entity注解即可。

运行代码测试,查看建表语句如下所示:

15:56:50,349 DEBUG SchemaExport:377 - 
    create table Person (
        id integer not null,
        name varchar(255),
        primary key (id)
    )
15:56:50,460 DEBUG SchemaExport:377 - 
    create table Student (
        id integer not null,
        name varchar(255),
        score integer not null,
        primary key (id)
    )
15:56:50,526 DEBUG SchemaExport:377 - 
    create table Teacher (
        id integer not null,
        name varchar(255),
        title varchar(255),
        primary key (id)
    )
15:56:50,592 DEBUG SchemaExport:377 - 
    create table t_gen_table (
         pk varchar(255),
         pk_value integer 
    ) 
看这里,生成了三张表,这三张表在存入数据的时候,ID都是从t_gen_table中去取的,而不可以是自动生成的。

第三种:JOINED

采用这种映射策略时,父类实例保存在父类表里,而子类实例则由父类表和子类表共同存储。因为子类实例也是一个特殊的父类实例,因此必然包含了父类实例的属性,于是将子类与父类共有的属性保存在父类表中;而子类增加的属性,则保存在子类表中。 在这种映射策略下,无须使用辨别者列,但需要为每个子类映射共有主键。而且如果继承树的深度很深,那么查询一个子类实例时,可能需要跨越多个表,因为子类的数据依次保存在其多个父类中。效率较低。

代码示例:(只给出Person的代码,Teacher、Student和上面的TABLE_PER_CLASS一模一样)

Person:

@Entity
@Inheritance(strategy=InheritanceType.JOINED)
public class Person{
	private int id;
	private String name;
	@Id
	@GeneratedValue
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
}
测试输出代码:

16:26:28,730 DEBUG SchemaExport:377 - 
    create table Person (
        id integer not null auto_increment,
        name varchar(255),
        primary key (id)
    )
16:26:28,789 DEBUG SchemaExport:377 - 
    create table Student (
        score integer not null,
        id integer not null,
        primary key (id)
    )
16:26:28,845 DEBUG SchemaExport:377 - 
    create table Teacher (
        title varchar(255),
        id integer not null,
        primary key (id)
    )
16:26:28,901 DEBUG SchemaExport:377 - 
    alter table Student 
        add index FKF3371A1B860C688C (id), 
        add constraint FKF3371A1B860C688C 
        foreign key (id) 
        references Person (id)
16:26:29,012 DEBUG SchemaExport:377 - 
    alter table Teacher 
        add index FKD6A63C2860C688C (id), 
        add constraint FKD6A63C2860C688C 
        foreign key (id) 
        references Person (id)
从输出可以看到,Studnet、Teacher这两个类均参照了Person的主键。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章