Java Persistence API
46.8.1. 概览
Java Persistence API为开发人员提供了一种对象/关系映射(O-R Mapping)工具,用来管理Java应用中的关系数据。Java Persistence包括以下三个方面:
?Java Persistence API (Java持久应用开发接口)
?查询语言
?对象/关系映射元数据
46.8.2. 实体
46.8.2.1. 什么是实体
实体是轻量级的持久的领域对象。通常,实体代
表
关于同志近三年现实表现材料材料类招标技术评分表图表与交易pdf视力表打印pdf用图表说话 pdf
了关系数据库中的一个数据表,而每个实体的实例对应了数据表中的一条记录。虽然实体可能会使用一些帮助类,但实体类才是JPA中主要的编程对象。
实体的持久状态代表了实体的持久值域或持久属性。通过对象/关系映射注解,这些值域或属性把实体与实体间的关系映射到数据库的关系数据模型中。
46.8.2.2. 实体类的一些需求
实体类必须符合以下要求:
?必须以javax.persistence.Entity注解进行标记;
?必须拥有一个公共或保护的无参数构造
方法
快递客服问题件处理详细方法山木方法pdf计算方法pdf华与华方法下载八字理论方法下载
,此外也允许拥有其他构造方法;
?实体类不能被声明为final。它的任何方法与任何持久的实例变量也不能被声明为final;
?如果实体的实例将会被作为分布对象进行值传递,例如作为会话Bean的参数或返回值,则该实体类必须实现Serializable接口;
?实体类的父类可以是实体类或非实体类,非实体类也可以继承自实体类;
?持久的实例变量必须声明为私有,保护或包私有,且只能由实体类的方法直接访问。客户端必须通过访问器或业务方法来访问实体的状态。
46.8.2.3. 持久类的持久值域与持久属性
可以通过实体的值域或JavaBean风格的属性访问实体的持久状态。这些值域或属性必须是以下类型之一:
?Java基本类型
?java.lang.String
?其他可序列化的类型,包括:
?Java基本类型的包装类
?java.math.BigInteger
?java.math.BigDecimal
?java.util.Date
?java.util.Calendar --------------//日程表
?java.sql.Date
?java.sql.Time
?java.sql.TimeStamp ---------------//印章
?自定义的可序列化类型
?byte[]
?Byte[]
?char[]
?Character[]
?Enumerated类型
?其他实体类或实体类的集合
?可嵌入类
实体可以自由选择使用持久值域或持久属性。如果使用持久注解标记实体的实例变量,则使用持久值域;如果使用持久注解标记实体的getter方法,则使用持久属性。但不能在同一个实体中同时标记持久值域和持久属性。
46.8.2.4. 持久值域
如果实体类使用了持久值域,持久机制将在运行期直接访问实体类的实例变量。所有没有用javax.persistence.Transient注解标记或没有标注为transient的值域都会被持久化到数据存储设备中。必须为这些实例变量指定对象/关系映射注解。
46.8.2.5. 持久属性
如果实体类使用了持久属性,实体类必须遵照JavaBean的命名规则。JavaBean 风格的属性使用getter与setter方法,这些方法通常基于实例变量的名称来命名。规则是,一个类型为Type,属性名为property的持久属性,它的getter名称应为getProperty,setter名称应为setProperty。如果这个属性是布尔类型,则也可以用isProperty来作为getter名称。
单值持久属性的getter与setter签名应如下:
集合持久值域或持久属性必须使用JPA支持的集合接口:
?java.util.Collection
?java.util.Set
?java.util.List
?java.util.Map
如果实体类使用了集合类型持久属性,它的getter与setter的类型必须为以上的集合类型,或这些集合类型的泛型变体。例如,如果一个Customer实体拥有一个集合持久属性来记录多个电话号码,它应该具有以下方法:
必须对持久属性的getter方法使用对象/关系映射注解,同时不能对被注解为@Transient或标记为transient的值域或属性使用映射注解。
46.8.2.6. 实体的主键
每个实体对象都必须具有一个唯一标识,例如客户实体可以使用客户号码作为标识。这个唯一标识称为主键,客户端利用它来定位实体中某个特定的实例。实体可以使用简单主键或复合主键。
使用简单主键时,可用javax.persistence.Id注解来标记主键属性或值域。
复合主键必须在主键类中定义,使用javax.persistence.EmbeddedId与javax.persistence.IdClass来标记。
简单主键与复合主键中的属性或值域,必须是以下Java类型之一:
?Java基本类型
?Java基本类型的包装类
?java.lang.String
?java.util.Date
?java.sql.Date
不能使用浮点类型作为主键。若使用自动生成的主键,只有整数类型主键是平台无关的。
46.8.2.7. 主键类
主键类必须符合以下需求:
?必须声明为public;
?如果使用基于属性的访问机制,主键类的属性必须声明为public或protected;
?必须拥有一个公共的无参数构造方法;
?必须实现hashCode()与equals(Object other)方法;
?必须可序列化;
?复合主键必须映射到或被表示为实体类中的多个值域或属性,也可以映射到或被表示为一个可嵌入类;
?如果主键类被映射到实体类的多个值域或属性,则其中的主键值域或属性的名称和类型必须与实体类中的对应值域或属性匹配。
以下是一个代表复合主键的主键类范例,orderId与itemId值域共同唯一标识了一个实体实例:
46.8.3.1. 实体关系的多重性
实体关系中存在四种多重性:一对一,一对多,多对一,与多对多。
一对一(One-to-one):一个实体实例关联到另一个实体的一个实例。通过在相关的持久值域或属性上使用javax.persistence.OneToOne注解来表示一对一关系。
一对多(One-to-many):一个实体实例关联到另一个实体的多个实例。通过在相关的持久值域或属性上使用javax.persistence.OneToMany注解来表示一对多关系。
多对一(Many-to-one):一个实体多个实例关联到另一个实体的一个实例,与一对多关系正好相反。通过在相关的持久值域或属性上使用javax.persistence.ManyToOne注解来表示多对一关系。
多对多(Many-to-many):一个实体的多个实例关联到另一个实体的多个实例。通过在相关的持久值域或属性上使用javax.persistence.ManyToMany注解来表示多对一关系。
46.8.3.2. 实体关系的方向
实体关系可以是双向或单向的。双向关系同时具有持有端与反向端。单向关系则只具有持有端。关系的持有端决定了持久机制运行环境如何对数据库中的关系进行更新。
46.8.3.2.1. 双向关系
在双向关系中,每个实体都拥有一个关系值域或属性引用另一个实体。一个实体类的代码可以通过这个关系值域或属性来访问关联的实体对象。如果实体具有一个关系值域,则称为此实体“知道”它所关联的对象。例如,如果一个Order(订单)实体知道它所持有的LineItem(订单明细项)实例,同时LineItem实体知道它所属的Order实例,则这是一个双向关系。
?双向关系中的反向端必须使用@OneToOne、@OneToMany、@ManyToMany注解中的mappedBy元素来引用持有端的关联属性。
mappedBy元素标注了关系持有者的关系值域或属性;
?多对一双向关系的多方不能定义mappedBy元素。多方必须是关系中的持有者;
?对于一对一双向关系,持有者是指包含关系外键的一方;
?对于多对多双向关系,持有者可以是关系中的任何一方。
46.8.3.2.2. 单向关系
在单向关系中,只有一个实体拥有关系值域或属性引用关联的另一方。例如, LineItem(订单明细项)实体应有一个关系值域引用其关联的Product(产品)实体,但Product实体不应具有到LineItem的引用。也就是说,LineItem知道Product,但Product并不知道对应的LineItem。
46.8.3.2.3. 查询与关系方向
Java Persistence查询语言经常需要通过关系来导航。关系的方向决定了一条查询是否可以从一端的实体导航到另一端。例如,一条查询可以从LineItem导航到Product但反之不然。对于Order与LineItem,由于它们是双向关系,查询可以沿双向导航。
46.8.3.2.4. 级联删除关系
由关系联结的实体往往依赖于关系另一端的实体而存在。例如订单的明细项是订单的一部分,一旦订单被删除了,相关的订单明细项也需要删除。这种关系称为级联删除关系(cascade delete relationship)。
级联删除关系通过@OneToOne与@OneToMany关系中的cascade=REMOVE元素来标记。例如:
实体支持类继承、多态关联与多态查询。实体类可以继承自非实体类,同时非实体类也可以继承自实体类。实体类可以是抽象或具体的。
46.8.3.3.1. 抽象实体
可以使用@Entity注解把抽象类标记为抽象实体。抽象实体与具体实体的唯一差别是它不能被实例化。
查询抽象实体类与查询具体实体类一样。查询抽象实体类时,该查询将对抽象实体类的所有具体子类进行操作。
46.8.3.3.2. 映射超类(Mapped superclass)
实体可以继承自包含持久状态与映射信息的非实体的超类。也就是说,这些超类没有被@Entity注解标记,因而不会被Java持久机制提供者作为实体进行映射。这种超类通常用于多个实体子类具有某些公共持久状态与映射信息的情况。
映射超类通过使用javax.persistence.MappedSuperclass注解进行标记。
映射超类可以是抽象类或具体类。不能对映射超类进行查询,且不能在EntityManager或Query操作中使用映射超类,在这些操作中,必须使用映射超类的实体子类。映射超类不能参与实体关系。
在数据库中没有对应映射超类的数据表,数据表映射是由实体子类定义的。例如,以上代码范例中的映射数据表是FULLTIMEEMPLOYEE与PARTTIMEEMPLOYEE,而不存在EMPLOYEE数据表。
46.8.3.3.3. 非实体超类
实体类可以从不包含任何持久状态与映射信息的非实体超类中继承,这些超类可以是抽象类或具体类。从非实体类中继承的状态都是非持久的,在非实体类中使用任何映射或关系注解都会被忽略。非实体类不能在EntityManager或Query操作中使用。
46.8.3.4. 继承结构映射策略
使用javax.persistence.Inheritance注解,可以配置Java持久机制如何将继承结构结构映射到关系数据库中。可以从以下三种映射策略中选择:
?把整个继承结构映射到一个数据表中;
?把每个具体实体类映射到一个数据表中;
?使用“表连接”策略,超类中的公共属性以及各子类中的独特属性分别映射到不同的数据表中。
通过设置@Inheritance注解的strategy元素来指定使用何种策略,取值可以从javax.persistence.InheritanceType的枚举元素中选取:
当没有在继承结构的根类中指定映射策略时,缺省的策略是InheritanceType.SINGLE_TABLE。
46.8.3.4.1. 单表映射继承结构策略
这种策略对应InheritanceType.SINGLE_TABLE映射类型。在这种策略中,继承结构中的所有实体类都被映射到一个单一的数据表中。这个数据表包括了继承结构中的实体类所有可能使用的持久值域或属性的映射字段。对某个特定的实体实例,若它不包含某些映射字段对应的值域或属性,则该字段取空值null。单表映射策略使用一个额外的区分列字段来区分每条记录映射的实际类型,可以使用javax.persistence.DiscriminatorColumn注解对这个字段映射进行配置,缺省情况下,该字段的名称为DTYPE,使用实体子类的名称作为标识。
这一策略对覆盖整个继承结构的多态关系与查询提供了很好的支持。但它要求映射到子类持久属性的字段必须定义为可取空值,且当继承层次较深时,数据表将含有大量字段。
46.8.3.4.2. 单表映射具体实体类策略
这种策略对应InheritanceType.TABLE_PER_CLASS映射类型。在这种策略中,每个具体类都被映射到一个独立的数据表中。具体子类中所有持久值域或属性,包括从超类中继承的持久值域和属性,都被映射到一个数据表对应的字段上。
这个策略对多态关系的支持并不好,而且在对整个继承结构进行查询时,往往需要使用SQL UNION子句或使用多条查询语句分别查询每个子类的数据表。EJB3.0规范规定,是否支持这种策略是可选的,因此并不保证所有持久机制提供者都支持此策略。
46.8.3.4.3. 子类连接策略
这种策略对应InheritanceType.JOINED映射类型。在这种策略把继承结构的父类映射到独立的数据表中,每个子类都使用仅仅包含其特有属性的独立数据表。也就是说,子类数据表中不包含继承而来的值域或属性。这些子表都具有表示主键的一个或多个字段,这个主键同时作为连接到父类数据表的外键。
这种策略对多态关系提供了很好的支持,但在实例化子类或对整个继承结构进行查询时,需要执行一个或多个额外的SQL JOIN操作。当继承结构较复杂时会对性能造成影响。
使用此策略时,父类映射数据表中包含区分列字段,默认字段名为DTYPE,字段类型为DiscriminatorType.STRING类型。若开发者自行定义关系数据模型,需要保证父类映射数据表中包含此字段。开发者可以使用javax.persistence.DiscriminatorColumn注解对这个字段映射进行配置。
46.8.4. 管理实体
实体由实体管理器javax.persistence.EntityManager的实例进行管理。每个EntityManager实例都关联到一个持久上下文中。持久上下文是在一个特定的数据存
储中一系列受管实体实例的集合。EntityManager接口定义了与持久上下文交互的方法。
46.8.4.1. 实体管理器Entity Manager
实体管理器EntityManager接口提供了创建与删除持久实体实例、通过主键查找实体、以及在实体中执行查询的方法。
46.8.4.1.1. 容器管理的实体管理器
使用容器管理的实体管理器时,EntityManager实例关联的持久上下文自动由容器注入到所有应用组件中。这些组件将在单一的JTA(Java Transaction Architecture)事务中使用这个EntityManager实例。
JTA事务通常涉及到应用组件之间的调用,这些组件往往需要访问单一的持久上下文来完成这个事务。当EntityManager通过javax.persistence.PersistenceContext注解注入到应用组件时,持久上下文自动被传递到当前的JTA事务中。同时,映射到同一持久单元的EntityManager引用将提供访问此事务中的持久上下文的接口。通过自动传递持久上下文,多个应用组件不需要相互传递各自的EntityManager实例就能对在同一个事务中运作。Java EE容器管理这些实体管理器的生存周期。
可以通过向应用组件注入实体管理器的方式获取一个EntityManager的实例:
46.8.4.1.2. 应用管理的实体管理器
使用应用管理的实体管理器时,持久上下文不会被自动传递到应用组件中, EntityManager实例的生存周期由应用进行管理。
当应用需要访问一个不会随JTA事务在多个EntityManager中传递的持久上下文时,可使用应用管理的实体管理器。此时,每个EntityManager都将创建各自的持久上下文。这些EntityManager与它们所关联的持久上下文需要由应用显式创建或清除。
应用可以先使用javax.persistence.PersistenceUnit注解向应用组件注入javax.persistence.EntityManagerFactory实例,然后使用该实例的createEntityManager()方法来创建EntityManager实例:
46.8.4.1.3. 使用EntityManager查找实例
EntityManager的find方法可以用来从数据存储中通过主键查找实体。
可以通过调用EntityManager上的方法来管理实体实例。实体实例总是处于四种状态之一:新建,受管,分离,已清除。
新建的实体实例没有持久标识,没有与任何持久上下文关联。
受管的实体实例拥有持久标识,与一个持久上下文关联。
分离的实体实例拥有持久标识,但没有与持久上下文关联。
已清除的实体实例拥有持久标识,与一个持久上下文关联,并被标记为
计划
项目进度计划表范例计划下载计划下载计划下载课程教学计划下载
从数据存储中清除该实例。
46.8.4.2.1. 持久化实体实例
新建的实体实例可以通过两种方式成为受管的持久实例。一是对这个实例使用persist方法,二是该与关联的实体上传递过来的级联持久化操作。第二种方式要求实体所关联的另一实体在关系注解中使用cascade=PERSIST或cascade=ALL设置。受管表示与持久操作相关的事务成功完成时,实体的持久状态将存储到数据库中。如果实体已经处于受管状态,持久化动作将会被忽略,但如果该实体设置了cascade=PERSIST或cascade=ALL,这一持久化动作仍然会被级联传递。如果对一个处于已清除状态的实体实例使用persist方法,则该实例会变为受管状态。如果对一个处于分离状态的实体实例使用persist方法,将会引起一个IllegalArgumentException异常,或者该事务会提交失败。
若进行持久化动作的实体关系注解的cascade元素设置为ALL或PERSIST,持久化动作将会级联传递到与之关联的所有实体实例。