[翻译] hibernate映射继承关系(一):一张表对应一整棵类继承树
2人收藏此文章, 我要收藏发表于1年前(2012-05-22 16:34) , 已有482次阅读 ,共0个评论
英文原址
网上这个主题的文章不在少数,这个系列的文章的部分价值在于给出了注解模式(Annotation)的例子。文章易懂,权当增强记忆,捎带着练习下翻译(翻译不当之处请指出)。
Hibernate中继承关系的简介:
java是一种面向对象语言,它可以实现继承关系。然而,继承却是"对象模型-关系模型"不匹配的最显而易见的方面之一。面向对象系统能够轻松的对“is a”和“has a”关系进行建模。而关系模型只能表达两个实体间的"has a"关系。hibernate能够把有关联的表映射为对象,但你需要根据需要来选择不同的映射策略。
Hibernate继承关系映射策略分为三种:一张表对应一整棵类继承树、一个类对应一张表、每一个具体类对应一张表。
之一:一张表对应一整棵类继承树(子类和父类共享同一张表)
假设我们有一个 Person 类及其子类 Employee. 每个类包括如下属性:
1
*
class
Person
2
- firstname
3
- lastname
4
5
*
class
Employee
6
- joining_date
7
- department_name
在“一张表对应一整棵类继承树”这种模式中,继承树上的所有类的数据都存储在一张表上,鉴别器字段(discriminator )是唯一的标识每个类的关键字段。
下面是“一张表对应一整棵类继承树”模式的优点和缺点:
优点
这种模式提供了最好的性能,因为即使在深层继承的情况下,检索一条子类数据,也只需要一次select操作。
缺点
对于任何一个子类的变更,比如增删改某字段,都将导致数据库表的变更。
建表语句
1
CREATE
TABLE
`person` (
2
`person_id`
BIGINT
(10)
NOT
NULL
AUTO_INCREMENT,
3
`firstname`
VARCHAR
(50)
NULL
DEFAULT
NULL
,
4
`lastname`
VARCHAR
(50)
NULL
DEFAULT
NULL
,
5
`joining_date`
DATE
NULL
DEFAULT
NULL
,
6
`department_name`
VARCHAR
(50)
NULL
DEFAULT
NULL
,
7
`discriminator`
VARCHAR
(20)
NOT
NULL
,
8
PRIMARY
KEY
(`person_id`)
9
)
PERSON表被用来同时存储 Employee 和 Person 对象.
Hibernate 继承: XML 映射
下面的例子展示了如何用XML方式映射 Employee 和 Person 实体类.
Person.java
01
package
net.viralpatel.hibernate;
02
03
public
class
Person {
04
05
private
Long personId;
06
private
String firstname;
07
private
String lastname;
08
09
10
}
Employee.java
01
package
net.viralpatel.hibernate;
02
03
import
java.util.Date;
04
05
public
class
Employee
extends
Person {
06
07
private
Date joiningDate;
08
private
String departmentName;
09
10
11
}
Person.hbm.xml
01
<?
xml
version
=
"1.0"
encoding
=
"UTF-8"
?>
02
<!DOCTYPE hibernate-mapping PUBLIC
03
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
04
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
05
06
<
hibernate-mapping
package
=
"net.viralpatel.hibernate"
>
07
08
<
class
name
=
"Person"
table
=
"PERSON"
discriminator-value
=
"P"
>
09
<
id
name
=
"personId"
column
=
"PERSON_ID"
>
10
<
generator
class
=
"native"
/>
11
</
id
>
12
13
<
discriminator
column
=
"DISCRIMINATOR"
type
=
"string"
/>
14
15
<
property
name
=
"firstname"
/>
16
<
property
name
=
"lastname"
column
=
"lastname"
/>
17
18
<
subclass
name
=
"Employee"
extends
=
"Person"
discriminator-value
=
"E"
>
19
<
property
name
=
"departmentName"
column
=
"department_name"
/>
20
<
property
name
=
"joiningDate"
type
=
"date"
column
=
"joining_date"
/>
21
</
subclass
>
22
</
class
>
23
</
hibernate-mapping
>
注意这里只定义了一个 hibernate 映射文件 Person.hbm.xml.
Person 和 Employee 类都定义在这同一个文件中.
<discriminator> 标签用来指定鉴别器列,包括列名和类型.
<subclass> 标签用于映射子类 Employee. 注意我们没有用常规的 <class>标签来映射 Employee ,因为它位于继承关系树下端。
Person 类的鉴别器的值被指定为 “P” ,相应的 Employee 是“E”,这样, 当Hibernate将要持久化person 或 employee时,相应的“P”或“E”将被置入鉴别器字段。
Hibernate 继承: 注解映射
下面的例子展示了如何用JPA注解方式来映射 Employee 和 Person 实体类。
Person.java
01
package
net.viralpatel.hibernate;
02
03
import
javax.persistence.Column;
04
import
javax.persistence.DiscriminatorColumn;
05
import
javax.persistence.DiscriminatorType;
06
import
javax.persistence.DiscriminatorValue;
07
import
javax.persistence.Entity;
08
import
javax.persistence.GeneratedValue;
09
import
javax.persistence.Id;
10
import
javax.persistence.Inheritance;
11
import
javax.persistence.InheritanceType;
12
import
javax.persistence.Table;
13
14
@Entity
15
@Table
(name =
"PERSON"
)
16
@Inheritance
(strategy=InheritanceType.SINGLE_TABLE)
17
@DiscriminatorColumn
(
18
name=
"discriminator"
,
19
discriminatorType=DiscriminatorType.STRING
20
)
21
@DiscriminatorValue
(value=
"P"
)
22
public
class
Person {
23
24
@Id
25
@GeneratedValue
26
@Column
(name =
"PERSON_ID"
)
27
private
Long personId;
28
29
@Column
(name =
"FIRSTNAME"
)
30
private
String firstname;
31
32
@Column
(name =
"LASTNAME"
)
33
private
String lastname;
34
35
36
}
Person 类是继承树的根类,所以我们使用了下面一些注解使其成为根类。
@Inheritance – 定义一个实体类继承树的继承策略,这个注解只能定义在继承树的根类上。
@DiscriminatorColumn – 用于当@Inheritance 的值被定义为 SINGLE_TABLE 或 JOINED 时,指定鉴别器列。 此注解只能用于两种类:一、继承树的根类,二、继承树的某子类,并且该子类定义了自己继承策略。
如果在需要鉴别器列的时候,没有使用@DiscriminatorColumn注解,那么鉴别器列的名称将默认为“DTYPE”,类型将默认为“STRING”。
@DiscriminatorValue – 用于指定给定实体类所对应的鉴别器列的具体值。 DiscriminatorValue 注解只能用在具体的实体类中。如果使用了鉴别器列,但是没有使用 DiscriminatorValue 注解,一个鉴别器值生成器将生效并产生一个鉴别器值来代表这个类。如果鉴别器列的类型是 STRING, 鉴别器列的值默认是该类的名称。如果不采用默认值,那么应该在继承树的每个类中都使用该注解。
Employee.java
01
package
net.viralpatel.hibernate;
02
03
import
java.util.Date;
04
05
import
javax.persistence.Column;
06
import
javax.persistence.DiscriminatorValue;
07
import
javax.persistence.Entity;
08
import
javax.persistence.Table;
09
10
@Entity
11
@Table
(name=
"PERSON"
)
12
@DiscriminatorValue
(
"E"
)
13
public
class
Employee
extends
Person {
14
15
@Column
(name=
"joining_date"
)
16
private
Date joiningDate;
17
18
@Column
(name=
"department_name"
)
19
private
String departmentName;
20
21
22
}
Employee类是Person类的子类,所以在映射时,使用@DiscriminatorValue注解来定义鉴别器的值,此例中,“E”将被置入鉴别器列。
MainClass
package net.viralpatel.hibernate;
01
import
java.util.Date;
02
03
import
org.hibernate.Session;
04
import
org.hibernate.SessionFactory;
05
06
public
class
Main {
07
08
public
static
void
main(String[] args) {
09
10
SessionFactory sf = HibernateUtil.getSessionFactory();
11
Session session = sf.openSession();
12
session.beginTransaction();
13
14
Person person =
new
Person(
"Steve"
,
"Balmer"
);
15
session.save(person);
16
17
Employee employee =
new
Employee(
"James"
,
"Gosling"
,
"Marketing"
,
new
Date());
18
session.save(employee);
19
20
session.getTransaction().commit();
21
session.close();
22
23
}
24
}
Main class
用来持久化 Person 和 Employee类的实例。注意两个类都存储在PERSON表中,鉴别器列用于区分两个实体。 输出结果
1
Hibernate:
insert
into
EMPLOYEE (FIRSTNAME, LASTNAME, discriminator)
values
(?, ?,
'P'
)
2
Hibernate:
insert
into
EMPLOYEE (FIRSTNAME, LASTNAME, department_name, joining_date, discriminator)
values
(?, ?, ?, ?,
'E'
)
需要例子中完整代码的,原文连接中提供有下载。