单个实体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 策略也是不符合规范的。