实体的继承
实体类支持类继承、多态关联和多态查询。实体类可以继承非实体类,非实体也也可以继承实体类。实体类可以是抽象类也可以是具体类。
roster实例应用演示了实体的继承,相关描述参见:“roster应用中的实体继承” page 620.
抽象实体
通过使用@Entity注解,一个抽象类可以被声明为一个实体。抽象实体类似体实体,但是抽象不能被实例化。
可以像查询具体实体一样查询抽象实体。如果抽象实体是查询对象,查询操作就执行在该抽象实体的所有具体实体上:
@Entity
public abstract class Employee {
@Id
protected Integer employeeId;
...
}
@Entity
public class FullTimeEmployee extends Employee {
protected Integer salary;
...
}
@Entity
public class PartTimeEmployee extends Employee {
protected Float hourlyWage;
}
带映射超类(Mapped Superclasses)
实体可能继承自超类,这些超类包括持久化状态和映射信息,但是并不是实体。这就是说,超类并没有使用@Entity注解休息,也没有被Java持久化提供商映射(到数据库)。当你有多个实体的通用状态及映射信息时,通常会使用这种超类。(TODO:此处最好举个例子说明。译注:如更新时间、更新者等字段)
带映射超类,可通过注解javax.persistence.MappedSuperclass指定:
@MappedSuperclass
public class Employee {
@Id
protected Integer employeeId;
...
}
@Entity
public class FullTimeEmployee extends Employee {
protected Integer salary;
...
}
@Entity
public class PartTimeEmployee extends Employee {
protected Float hourlyWage;
...
}
带映射超类不能够被查询,也不可以用于EntityManager及Query操作。你只能使用其实体类的子类。带映射超类也不可以用于指定实体关系。带映射超类可以是抽象的也可以是具体的。
在底层数据库中,带映射超类没有任何关联的表。继承自带映射超类的实体定义(实体到)表的映射。例如在代码示例中,底层表是FULLTIMEEMPLOYEE和PARTTIMEEMPLOYEE,但是却没有表EMPLOYEE。
非实体超类
实体可以有非实体超类,而这些超类也可以是抽象类或具体类。非实体超类的状态是非持久化的,而且实体类继承自非实体超类的状态也是非持久化的。非实体超类不可以用于EntityManager及Query操作。非实体超类上的任何映射或者关系注解都会被忽略。
实体继承的映射策略
You can configure how the Java Persistence provider maps inherited entities to the underlying datastore by decorating the root class of the hierarchy with the annotation javax.persistence.Inheritance. The following mapping strategies are used to map the entity data to the underlying database:
你可以使用注解javax.persistence.Inheritance修饰继承树的根类,以配置如何将实体映射到底层数据库。可使用下列映射策略将实体映射到底层数据库:
■ 每套实体类对应一个表
■ 每个具体实体类对应一个表
■ 一种“连接”策略,这种策略下,子类特有字段或特性被映射到单独的表,而不是用于保存父类通用字段或特性的表。
这种策略通过设定@Inheritance注解的strategy元素来配置,可选的配置项定义在枚举类型javax.persistence.InheritanceType中:
public enum InheritanceType { SINGLE_TABLE,
JOINED, TABLE_PER_CLASS
};
如果继承树上的根类没有指定@Inheritance注解,则会使用默认的策略: InheritanceType.SINGLE_TABLE。
一套实体类对应一张表的策略
这种策略下,即使用默认值InheritanceType.SINGLE_TABLE时,层级中所有类都映射到数据库中的一个表中。这个表有一个区分(discriminator)列,该列的值标识了一行记录所代表的实例是哪个子类。
区分列的元素如表 32–2所示,可以在类层级的根类上,使用注解javax.persistence.DiscriminatorColumn 指定。
表 32–2 @DiscriminatorColumn的元素
类型 名称 描述
String Name 用来作为区分列的实际列的名称。默认值是DTYPE这个元素是可选的。
DiscriminatorType discriminatorType 区分列的类型,默认值是DiscriminatorType.STRING。这个元素是可选的。
String columnDefinition 用来创建区分列的SQL语句。默认由持久化供应商生
成,而且这个是根据具体实现不同而不同。
这个元素是可选的
String Length 当类型是DiscriminatorType.STRING时,此字段为列的长度,对于非STRING类型,此字段会被忽略。
其默认值是31,这个元素是可选的。
The javax.persistence.DiscriminatorTyp枚举类型是用来设定数据库中的区分列类型的。@DiscriminatorColumn注解的discriminatorType元素使用其作为参数值,DiscriminatorTyp的定义如下:
public enum DiscriminatorType { STRING,
CHAR, INTEGER
};
如果实体层级的类实体没有指定@DiscriminatorColumn,而又需要一个区分列时,持久化供应商会假定默认的区分列名为DTYPE列的类型为DiscriminatorType.STRING。
可以用javax.persistence.DiscriminatorValue注解来为每层实体设置区分列的值。你可以用@DiscriminatorValue只修饰具体实体。
如果指定了区分列,但是未设定@DiscriminatorValue的值,持久化供应商会提供一个默认的,跟具体实现有关的值。如果@DiscriminatorColumn的discriminatorType元素是DiscriminatorType.STRING,那默认的值就是实体名称。
这个策略良好地支持了实体层级间的多态关系。但是这种策略需要子类的属性(state)可为空。
每个具体类一张表的策略
这种策略下,即指定为InheritanceType.TABLE_PER_CLASS时,每个具体映射到一个数据库中的单独的一个表。类中所有字段和特性,包括继承的字段和特性,都映射到该类自己的表上的字段中。
这种策略对多态关系支持的不好,在需要涵盖整个实体层级的查询查询,通常需要使用SQL的UNION语句来合并查询,或者分别查询每个子类。
This strategy provides poor support for polymorphic relationships and usually requires either SQL UNION queries or separate SQL queries for each subclass for queries that cover the entire entity class hierarchy.
对这种策略的支持是可选的,而且持久化供应商可能会不支持。GlassFish服务器中,默认的JPA供应商就不支持这种策略。
连接子类策略
这种策略,即指定为InheritanceType.JOINED时,类层级的根类映射到一张表,然后其其每个子类分别映射到一个表,其只包括子类特有的字段。这就是说,子类的表不包括继承来的字段或特性。子类表也有一个(或一些)列来标识它的主键,子类表的主键也是父类表的主键的外键。
这种策略提供了对多态关系的良好支持,但在实例化实体子类时,需要至少一次做一次连接操作。这可能会导致处理扩展类层级时存在性能问题。类似的,查询整个实体类层级需要对子表做连接操作,也会导致性能降低。
一些JPA供应商,包括GlassFish服务器中默认的供应商,在使用连接子类策略时,需要一个区分列来标识根实体。如果你的应用没有使用自动建表,请确认数据库表正确配置了默认区分列,或者使用@DiscriminatorColumn注解映射到你数据库的schema中。更多关于区分列的信息,参见“每套类层级一个表策略” on page 597.