Richie

Sometimes at night when I look up at the stars, and see the whole sky just laid out there, don't you think I ain't remembering it all. I still got dreams like anybody else, and ever so often, I am thinking about how things might of been. And then, all of a sudden, I'm forty, fifty, sixty years old, you know?

JPA规范的主要内容

Entity Identity 实体标识
一般采用单一值做为entity identity
采用compsite primary key时,必须单独建立一个表示主键的类-primary key class主键类。这样在实体上compsite primary key就可以用一个属性(primary key class类型)表示
持久化框架内部很多地方使用entity identity进行处理,因此primary key class要正确实现equals、hashCode方法
另外应用层需要注意,对persistence context中受管理的对象,不要修改主键值,否则持久化框架无法承诺带来的影响,因为persistence context只能基于entity identity标识实体身份

Accessor of Persistent Fields and Properties 持久化值域和属性的访问器
Persistent state(即需要持久化的fields或properties)的存取分2种类型:field-based和property-based
field-based时持久化框架直接存取instance variables,property-based时则通过getter、setter方法存取
使用property-based时,在setter、getter方法中可能包含业务逻辑,对于应用层而言,需要注意的是持久化框架从数据库加载数据,为实体属性赋值时,各属性的赋值顺序是无法承诺的,因此getter、setter中的业务逻辑需要注意这一点
另外使用property-based时如果还将属性声明为lazy-fetching,则应用层不应当访问相应的instance variable,因为持久化框架可能对setter、getter进行注入以实现lazy-fetching,直接访问instance variable可能获取不到值
对于集合类型,JPA规定必须使用java.util下面的Collection、Set、Map、List这几个接口,关于List、Set、Map、Bag的语义,参考这篇post

Persistence Context 持久化上下文

   
Persistence Context是持久化框架需要维护的一个中间容器,其中会存放受管理对象的副本,例如从数据库获取的实体对象,或者调用持久化框架保存过的对象等
主要作用有:缓存(例如NHibernate的一级缓存概念)、支持Flush操作、辅助实现实体以及属性的Dirty检查等
JPA将persistence context分为2种类型:PersistenceContextType.TRANSACTION和PersistenceContextType.EXTENDED。TRANSACTION类型(默认类型)的persistence context生命周期与transaction scope一致,例如事务开始时为生命周期的开始阶段,事务提交或回滚时生命周期结束。EXTENDED类型的生命周期基本与EntityManager一致,在EntityManager创建时开始,在EntityManager关闭时结束。实现Extended类型的persistence context时需要注意其生命周期可能跨越多个事务以及不在事务范围内的情况,伴随的是这些情况下对persistence context中实例对象生命周期以及相关操作的影响,例如version控制等

Entity Instance's Life Cycle 实例对象生命周期
实例对象的生命周期可以参考一下这篇post
实例对象的生命周期有4种状态:
New: 新创建的实例对象,没有identity值
Managed: 在持久化上下文中受管理的对象
Detached: 游离于持久化上下文之外的实例对象
Removed: 被删除的实例对象
JPA中详细定义了实例对象伴随着EntityManager的各种操作时,生命周期状态间的转换关系

JPA声明了entity instance life cycle的几个callback方法:PrePersist、PostPersist、PreRemove、PostRemove、PreUpdate、PostUpdate、PostLoad,对其语义进行了比较详细的说明

Entity Relationships 实体关系
JPA定义了one-to-one、one-to-many、many-to-one、many-to-many 4种关系

Owning side, Inverse side 关系的所有方、反向方
在关联关系的2方之中,关系的所有者一方为owning side,另一方为inverse side。关系的所有者可以这样来理解,将所有者一方的实例删除后,这个2方关系的任何信息都彻底消失。假如逻辑上表A有一个外键,引用自表B的主键(所谓逻辑上是指数据库中并没有建立这个外键关系),如果将表B中某条数据b1删除,A、B间相应实例对象的关联关系可能并不会彻底消除,因为表A中可能还存在某数据a1引用了b1的主键值,因此与B对应的实体类就不是owning side了
因此拥有外键的一方就是owning side了,这个在one-to-one的关系中比较清晰;one-to-many、many-to-one中,many的一方为owning side;many-to-many中任何一方都可以作为owning side
Owning side、inverse side将作为cascade相关操作的依据之一

JPA详细定义了默认情况下对以下一些映射的处理方式:
双向one-to-one;双向one-to-many、many-to-one;单向single-valued(单向one-to-one、many-to-one);双向many-to-many;单向multi-valued(单向one-to-many、many-to-many)

Inheritance 继承
Inheritance Mapping Strategies包括:
Single table per class hierarchy,使用一个discriminator column区别子类,优点是很好的支持多态关系、查询,缺点是子类独有的字段需要允许为null
Table per concrete entity class,缺点是不好支持多态关系,通常需要对各子类分别使用单独的SQL或者使用UNION语句
Joined subclass,与table per concrete entity class区别是,将公共属性的superclass使用一个表存储,这种策略也能较好的支持多态关系,缺点是需要多个JOIN关系
详细参考这篇post

Optimistic Locking and Concurrency 乐观锁及并发控制
JPA默认应用层将使用乐观锁机制
使用乐观锁时entity必须声明一个version属性,该字段的值由持久化框架自动维护,应用层不能修改。持久化框架可以引入其它机制实现乐观锁检查,或者实现更细粒度的乐观锁控制,JPA不做要求
JPA的乐观锁机制是基于数据行的

JPA中声明了Lock Mode,可以实现悲观锁效果,至于实现方式不作具体要求,只要确保不存在P1: dirty read、P2: non-repeatable read就行
LockModeType有READ和WRITE 2种
对于WRITE模式,在isolation level为read commited情况下数据库可以确保不会出现P1、P2两种状况
READ模式一般使用数据库的锁实现,用它实现悲观锁效果

Query Language 查询语言
类SQL的语法,但是带上了较明显的对象特征
声明了SELECT、UPDATE、DELETE 3种语句

用简单的例子浏览一下大致特性
1. 使用实体间的关联关系
SELECT DISTINCT o
FROM Order o JOIN o.lineItems l JOIN l.product p
WHERE p.productType = 'office_supplies'
JOIN子句不再需要ON条件部分了,因为对象之间已经定义了关联关系

2. Collection Member Declarations
SELECT DISTINCT o
FROM Order o, IN(o.lineItems) l
WHERE l.product.productType = 'office_supplies'
与1中的语句是等效的
FROM子句中为Order实体定义了别名o(Identification Variables);o.lineItems是集合类型的属性,IN(o.lineItems) l 的语义是:Order实例对象的lineItems中必须有符合后续条件的,l则代表lineItems中的每个对象;WHERE子句进一步声明了对l的过滤条件

JPA中把l.product这种表达式叫做path expression(An identification variable followed by the navigation operator (.) and a state-field or association-field is a path expression)
path express中每个点号后面的内容,有三种类型:persistent field、single-valued relationship field、collection-valued relationship field。如果是collection-valued relationship field则只能在特定的场景下使用
例如上面1、2例子中,如果lineItems不是集合类型,则这2个例子的语句都等效于下面的写法(lineItems属性改名为lineItem以区别于集合这个概念):
3. 
SELECT DISTINCT o
FROM Order o
WHERE o.lineItem.product.productType = 'office_supplies'
对path expression中的关联关系,JPA规定使用INNER JOIN操作,因此上面语句等效前面的示例1、2
但当lineItems为collection-valued relationship field时,上面写法不支持,必须采用1或2的写法
特定场景包括上面示例出来的JOIN子句、Collection Member Declarations这2种,还有Empty Collection Comparison Expressions、Collection Member Expressions中可以使用

4. Empty collection comparison expressions
SELECT o
FROM Order o
WHERE o.lineItems IS [NOT] EMPTY

5. Collection Member Expressions
SELECT o
FROM Order o
WHERE :lineItem MEMBER [OF] o.lineItems
:lineItem是一个变量,在应用中为该变量赋值一个lineItem实例对象,上面查询的语义是:找到:lineItem对象关联的Order实例对象

6. ALL and ANY Expressions
SELECT emp
FROM Employee emp
WHERE emp.salary > ALL (
    
SELECT m.salary
    
FROM Manager m
    
WHERE m.department = emp.department)
ALL和ANY表达式用于子查询
上面的emp.salary>ALL(...),表示emp.salary必须大于子查询中返回的所有值;如果子查询没有符合条件的记录,则emp.salary>ALL(...)这个条件为true
将上面例子中的关键字ALL改为ANY(或者是SOME)关键字,则表示emp.salary只要大于子查询中返回的部分值就符合条件;如果子查询没有符合条件的记录,则emp.salary>ANY(...)这个条件为false;如果子查询返回的结果全部都大于或等于emp.salary,则这个条件为false

7. Constructor Expressions
使用constructor expressions可以指定使用特定的构造函数返回实例对象列表,而不是object数组的列表
SELECT NEW com.xyz.CustomerDetail(c.name, c.country.name)
FROM customer c
WHERE c.lastname = 'Coss' AND c.firstname = 'Roxane'

8. 再来几个示例
Find all orders that have line items:
SELECT DISTINCT o
FROM Order o, IN(o.lineItems) l
或者:
SELECT o
FROM Order o
WHERE o.lineItems IS NOT EMPTY

Find all orders that have no line items:
SELECT o
FROM Order o
WHERE o.lineItems IS EMPTY

通过上面这些例子可以看出JPA的查询语言具备较强的对象表达能力

UPDATE、DELETE语句的主要目的是提供bulk update、delete操作
JPA中也声明了一些函数,大部分为SQL标准中的一些基本函数

实现JPA规范的查询语言时,需要重点处理的一个方面是多态查询,语句中的实体类名有可能是abstract的或者supperclass,查询解析器需要能够处理这种多态查询。例如NHibernate(目前版本不知是否改成了Antlr来解析)在分析查询语句时比较简化处理,每遇到实体类都搜索是否有子类存在,如果有则会为每个子类生成一个查询语句,使用JOIN、UNION或者单独执行每个语句再将结果合并(NHibernate不支持Union Subclass),只实现了部分多态能力。要实现完全的多态能力,面对的情况以及需要处理的东西都比较复杂
其它一些细节方面的东西也非常多,例如persistence context可能缓存了一些更新操作,等待Flush时回写数据库,但为了保证查询结果的一致性,执行查询前至少需要先将persistence context中相关实体的更新缓存运用到数据库中

Metadata
详细的元数据声明规范,查阅JPA对annotation、XML descriptor的说明一节

posted on 2009-01-06 23:18  riccc  阅读(13412)  评论(0编辑  收藏  举报

导航