EJB学习日记(17)

本文介绍了Java持久化规范中单一表继承映射策略(SINGLE_TABLE),该策略将所有继承层次的实体映射到同一张数据库表中,并通过辨别器列区分具体类型。文章详细解析了相关注解的使用及其实现细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

2007年10月10日 06:56:00

单个实体BEAN的映射到数据库的方式很简单,但是如果我们的实体BEAN之间存在着继承关系呢?在数据库里面将如何表现这种继承关系?
JAVA持久化规范里面提供了三种方式去处理继承实体的映射方式:
一,所有继承层次共单独一张表
二,每个具体的类一个单独的表
三,每个子类一张表

为了更好的举例说明,我们构造出如下的继承层次,以做为例子使用。


我们今天先来看看第一种方式,那就是所有的继承层次共单独一张表。

一,所有继承层次共单独一张表

在这种模式中,一张数据库的表里面将放入所有的继承层次的类的属性,在我们的例子中,我们的Person,Customer,Empolyee的实体都将映射在同一张表里面,表的结构如下所示:

create table PERSON_HIERARCHY
(
id
integer primary key not null ,
firstName
varchar ( 255 ),
lastName
varchar ( 255 ),
street
varchar ( 255 ),
city
varchar ( 255 ),
state
varchar ( 255 ),
zip
varchar ( 255 ),
employeeId
integer ,
DISCRIMINATOR
varchar ( 31 ) not null
);

正是因为我们把所有继承层次的实体都放在同一张表里面,所以我们需要一个来标志具体类型的列,它指示当前记录是属于哪个类的,这样 EntityManager好还原成相应的实体BEAN而不致于出错。我们还是先看看代码是如何告诉EntityManager它的继承实现方式的。

@Entity
@Table(name
= " PERSON_HIERARCHY " )
@Inheritance(strategy
= InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name
= " DISCRIMINATOR " ,
discriminatorType
= DiscriminatorType.STRING)
@DiscriminatorValue(
" PERSON " )
public class Person {
private int id;
private String firstName;
private String lastName;

@Id @GeneratedValue
public int getId( ) { return id; }
public void setId( int id) { this .id = id; }

public String getFirstName( ) { return firstName; }
public void setFirstName(String first) { this .firstName = first; }

public String getLastName( ) { return lastName; }
public void setLastName(String last) { this .lastName = last; }
}

@javax.persistence.Inheritance注释就是用来声明继承的时候它的持久化策略的,它的声明如下:

package javax.persistence;

@Target(TYPE) @Retention(RUNTIME)
public @ interface Inheritance {
InheritanceType strategy( )
default SINGLE_TABLE;

}

public enum InheritanceType {
SINGLE_TABLE, JOINED, TABLE_PER_CLASS
}

在这里,strategy()方法定义了我们所使用的继承映射模式,我们在这里用的是单独一张表放所有的继承层次实体,所以我们使用了枚举 InheritanceType.SINGLE_TABLE,有一点我们需要注意的是,@Inheritance这个注释仅仅只在继承层次的根类上是必须 要有的,一般它的子类都没有必要写这个注释,除非你想改变继承映射的实现方式。

package javax.persistence;

@Target(TYPE) @Retention(RUNTIME)
public @ interface DiscriminatorColumn
String name( )
default " DTYPE " ;
DiscriminatorType discriminatorType( )
default STRING;
String columnDefinition( )
default "" ;
int length( ) default 10 ;
}

因为我们使用一张表来保存所有继承层次的类,所以我们需要一个某种方式好让持久化实现者知道如何去分辨我们真正想要保存的对象是属于哪个继承层次 的,我们靠从一个辨别器的列里面去获得这一点。@javax.persistence.DiscriminatorColumn这个流释就是指示我们哪个 类将会存储辨别器,看着注释我们可以知道,这个注释并不是必要的,因为它每项都有默认值,对于辨别器的类型,默认是String类型,我们除了 String类型之外,还可以用如下几个类型:char,Integer。

package javax.persistence;

@Target(TYPE) @Retention(RUNTIME)
public @ interface DiscriminatorValue {
String value( )
}

这个注释是指示我们辨别器的值是多少,这个只是我们提示辨别器的类型是String的时候,还需要我们去写,如果类型是int或者char的时候,是不需要我们去提定它们的辨别器的值的。所以最好还是使用char或者int类型,以使我们从这些细节方面解放出来。

在我们建立了这种映射策略之后,子类的定义就显得简单多了:

@Entity
@DiscriminatorValue
(
" CUST " )
public class Customer extends Person {
private String street;
private String city;
private String state;
private String zip;
public String getStreet( ) { return street; }
public void setStreet(String street) { this .street = street; }

}

我们也可以都用默认的值,什么额外的注释都不要加

@Entity
public class Employee extends Customer {
private int employeeId;

public int getEmployeeId( ) { return employeeId; }
public void setEmployeeId( int id) { employeeId = id; }
}

在这个例子里面,Customer实体的辨别器列的值设为CUST,这是我们人为设置的。当然我们如果不设置的话,就像Employee,那么它的辨别器列的值就会被设为Employee, 因为它的类的名字就是Employee。

优点:
SINGLE_TABLE的映射策略是最简单的实现并且性能来说,也是比其它两个要高。因为它只有一张表需要去处理。持久化引擎不需要去做任何复杂的连接组合或者子查询等等,因为所有的的数据都存在一张表里面
缺点:
这种策略最大的一个缺点就是所有的有关子类的属性的映射列都必须是nullable,因为你不可能让一个类拥有所有的属性,毕竟这些属性是所有的类加起来 的,所以你不能为你的类加上NOT NULL的约束。还有,因为所有子类的属性列对于某些实体类来说都是没有用的,所以SINGLE_TABLE 策略也是不符合规范的。


来自: 千里冰封

Trackback: http://tb.blog.youkuaiyun.com/TrackBack.aspx?PostId=1817684


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值