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