在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的主键。