在ORM的概念大行其道之际,JavaEE平台也不得不做出适当调整,直接在平台层提供了对象/关系映射机制JPA,并结合了其重要的Entity JavaBean。JPA概括起来包括如下3个部分:
- 对象/关系映射元数据及API
- Java对象查询语言
- Java Criteria查询API
JPA 中的Entity是一种可持久化的域对象。一个Entity类对应关系数据库中的一张表,一个Entity实例对应关系数据库中的表的一行记录。
一个Entity类的定义,需要遵守如下规则:
- 由@javax.persistence.Entity标注
- 至少拥有一个public或protected的、无参数的构造函数
- 不能是final的类,也不能包含任何final的方法或可持久化的类变量
- 可持久化的类变量不能是public的,只能被Entity类中的方法直接访问(可以通过getter方法被应用间接访问)
- 可以继承自任何类(Entity类或非Entity类),也可以被任何类继承
- 必要情况下实现Serializable接口
1)持久化字段
Entity类中的可持久化的类变量也被称为持久化字段。持久化字段的持久化标注是定义在类变量上的。通常,持久化字段将被存储到对应的数据库中,除非以下情况:
- 持久化字段同时被@javax.persistence.Transient标注
- 持久化字段同时被Java关键字transient修饰
持久化属性的持久化标注是定义在类变量的getter方法上的。持久化属性都必须拥有getter/setter方法。如果类变量被如下定义,则不能在该类变量的getter方法上设置持久化标注,即这样的类变量不可能同时是持久化属性:
- 类变量被@javax.persistence.Transient标注
- 类变量被Java关键字transient修饰
2. Entity Relationship
Entity和Entity之间的关系称为Entity Relationship,Entity关系的表示是通过Entity类中的关系字段或关系属性。Entity关系的类型(用以修饰关系字段或关系属性的标注)如下:
- @javax.persistence.OneToOne
- @javax.persistence.
OneToMany
- @javax.persistence.
ManyToOne
- @javax.persistence.ManyToMany
1)Entity关系的方向性
Entity关系具有方向性,一个Entity关系所涉及的双方Entity,必然有一个owning方,另外还可以有一个inverse方。
Entity关系的定义可以是单向的,也可以是双向的:
- 单向,相互关联的两个Entity只有一个通过关系标注引用对方
拥有标注的这个Entity被称为关系的owning方(对应数据库的表中有外键)
- 双向,相互关联的两个Entity都通过关系标注彼此互相引用对方
其中一个Entity是关系的owning方,另一个Entity是关系的inverse方(标注中带有mappedBy)。
注意:
在
OneToOne
的关系中,对应数据库的表中有外键的是
owning
方;
在OneToMany
或
ManyToOne
的关系中,多方必须是
owning
方(外键必须在多方);
在ManyToMany的关系中,任意一方都可以是
owning
方(两方都有外键);
Entity Relationships的方向性决定了在查询时从一个Entity是否可以访问关联的Entity。
2)Entity关系的级联操作Cascade
3. Entity类的继承
Entity类支持类的继承和多态。Entity类(@javax.persistence.Entity标注修饰)与非Entity类(没有任何持久化标注修饰)之间可以互相继承。
Entity类可以是抽象类,也可以是具体类。抽象Entity类可以被用于查询语句,也可以被EntityManager管理。但因为抽象类不能被实例化,所以查询语句中查询的对象如果是抽象Entity类,则事实上是对该抽象Entity类的所有具体类的查询。
非Entity类中定义的任何持久化字段或持久化属性都将被忽略。即使是非Entity类被Entity类继承了,但是在非Entity类中包含的任何持久化字段或持久化属性仍将被忽略。非Entity类不能被用于查询语句,也不能被EntityManager管理。
有一种特殊的非Entity类被称为Mapped Superclass(@javax.persistence.MappedSuperclass标注修饰),其中也可以包含持久化字段或持久化属性。Mapped Superclass往往定义通用的持久化字段或持久化属性,并被Entity类继承。Mapped Superclass可以是抽象类,也可以是具体类。Mapped Superclass因为不是Entity,所以不能被用于查询语句,也不能被EntityManager管理。
对于一组有继承关系的Entity类,他们与数据库中的表之间的映射策略可以配置。其中处于继承关系顶端的根Entity类必须使用@javax.persistence.Inheritance标注。具体的映射策略如下:
- 整个继承关系的Entity类一起对应数据库中的一个表(默认)
@javax.persistence.Inheritance(strategy=”javax.persistence.InheritanceType.SINGLE_TABLE”)
- 每个具体的Entity类对应数据库中的一个表(不推荐)
@javax.persistence.Inheritance(strategy=”javax.persistence.InheritanceType.TABLE_PER_CLASS”)
- “join”策略,通用父Entity类和具体子Entity类都对应数据库中的一个表,使用时连接起来
@javax.persistence.Inheritance(strategy=”javax.persistence.InheritanceType.JOINED”)
4. 对Entity的管理EntityManager
javax.persistence.EntityManager对象负责管理位于同一数据库中的所有Entity。
一个EntityManager对象就对应一个持久化上下文(persistencecontext),即一个特定的数据存储,所有该数据存储中的Entity实例都由对应的EntityManager对象管理。事实上,持久化上下文实现对Entity实例的具体操作,而javax.persistence.EntityManager接口中定义的方法只是用于与持久化上下文交互。
- Container-Managed Entity Manager
在JTA事务中往往要访问持久化上下文,这时只要将该持久化上下文对应的EntityManager注入到应用中即可。对于JavaEE容器管理的EntityManager,无需在应用类之间传递EntityManager,JavaEE容器负责EntityManager的生命周期管理,并且在整个JTA事务中可用。
@javax.persistence.PersistenceContext
EntityManager em;
- Application-Managed Entity Manager
由于javax.persistence.EntityManager的实例不是线程安全的,为了线程安全地控制对数据库的访问,应用可以使用javax.persistence.EntityManagerFactory创建一个特有的EntityManager并关联一个独立的持久化上下文(称为持久化单元)。持久化单元的定义位于persistence.xml 文件中。
通过EntityManagerFactory创建的EntityManager,必须嵌入到事务操作的开始和结束之间才能够加入事务,即应用管理的EntityManager只能在应用管理的事务中使用。应用必须EntityManager及其持久化上下文的生命周期。
@javax.persistence.PersistenceUnit
EntityManagerFactory emf;
EntityManager em = emf.createEntityManager();
5. EntityManager对Entity的生命周期管理
Entity实例有如下4种状态:
- new,没有与持久化上下文关联,也没有持久标识
- managed,与持久化上下文关联,也有持久标识
- detached,没有与持久化上下文关联,但因为曾经关联过,所以有持久标识
- removed,与持久化上下文关联,也有持久标识,将被定期从数据库中删除
Entity实例中的数据与数据库中数据进行同步的发生:
- Entity实例所在的事务被提交
- 或者调用了EntityManager的flush()
6. 对Entity的查询
JPA提供如下2种查询Entity的方法:
- Java Persistence query language (JPQL),类似于SQL
查询结果需要进行类型转换
- Criteria API, 一组Java API
类型安全
执行性能高
编程复杂
7. JPA的锁机制
- 乐观锁(默认)
数据库的表中有version列(Entity类中的字段或属性使用@javax.persistence.Version标注),应用只能读取不能修改。
事务提交时申请short-termlock,失败则抛出javax.persistence.OptimisticLockException异常并回滚事务。
- 悲观锁,适合于数据变化特别频繁的数据库
事务在创建的时候就开始持有long-termlock,直至事务结束。
JPA提供的锁模式:
- javax.persistence.LockModeType.OPTIMISTIC
- javax.persistence.LockModeType.OPTIMISTIC_FORCE_INCREMENT
- javax.persistence.LockModeType.PESSIMISTIC_READ
- javax.persistence.LockModeType.PESSIMISTIC_WRITE
- javax.persistence.LockModeType.PESSIMISTIC_FORCE_INCREMENT
- javax.persistence.LockModeType.READ(不建议使用,等价于OPTIMISTIC)
- javax.persistence.LockModeType.WRITE(不建议使用,等价于OPTIMISTIC_FORCE_INCREMENT)
- javax.persistence.LockModeType.NONE
8. JPA的二级缓存
二级缓存是在JavaEE服务器上对一个持久化上下文中Entity的本地存储,对于应用来说是透明的。
JPA提供的缓存模式:
- ALL,缓存持久化上下文中的所有Entity
- NONE,不缓冲任何Entity
- ENABLE_SELECTIVE,只缓存@javax.persistence.Cacheable标注的Entity
- DISABLE_SELECTIVE,只不缓存@javax.persistence.Cacheable(false)标注的Entity
- UNSPECIFIED,使用JavaEE服务器的默认设置
1)缓存的Retrieval Mode,用于在调用EntityManager.find()方法时,控制数据从缓存中读取还是从数据库中读取。
JPA提供了2种RetrievalMode:
- javax.persistence.CacheRetrieveMode.USE
优先读取缓存,如果缓存再没有再读取数据库
- javax.persistence.CacheRetrieveMode.BYPASS
无论何时都直接读取数据库
2)缓存的Store Mode,用于控制数据在缓存中的存储。
JPA提供了2种StoreMode:
- javax.persistence. CacheStoreMode.BYPASS
对数据库的任何操作,都不改变缓存
- javax.persistence.CacheStoreMode.USE
对数据库中数据的读写将导致在缓存中同步创建或修改数据
如果缓存中已有该数据,则对该数据的后续读操作将不会刷新缓存
- javax.persistence. CacheStoreMode.REFRESH
对数据库中数据的读写将导致在缓存中同步创建或修改数据
如果缓存中已有该数据,则对该数据的后续读操作仍然会刷新缓存