引言
随着信息量的爆炸式增长,以及信息化应用的不断深入,缓存(Cache)对应用程序性能和数据库访问的优化变得越来越重要。将所需服务请求的数据存储到缓存中,可以提高应用程序访问数据的速度,减少用户的等待时间,从而让用户获得更好的用户体验。
JPA 是 Java Persistence API 的简称,JPA 1.0 目前只支持一级缓存。JPA 2.0 在 2009 年 12 月发布,作为 Java 6 的标准,JPA 2.0 增加了对二级缓存的支持。二级高速缓存通常是用来提高性能的,它可以避免访问已经从数据库加载的数据对象,提高访问未被修改的数据对象的速度。
JPA 一级缓存和二级缓存的缓存机制和存储范围
JPA 一级缓存
JPA 目前支持两种缓存,一级缓存和二级缓存。目前 JPA 1.0 不支持二级缓存。持久化上下文其实就是 JPA 的一级缓存,通过在持久化上下文中存储持久化状态实体的快照,既可以进行脏检测,还可以当做持久化实体的缓存。一级缓存属于请求范围级别的缓存。
图 1. JPA 一级缓存

如图 1 所示,持久化上下文可以理解成一个数据库。实体管理器需要保证在持久化上下文中,每一个数据库行都只对应一个实体。但同一实体又可以被另外一个实体管理器管理。所以在一级缓存的范围内,需要借助乐观锁或悲观锁来保证实体操作的正常执行。
清单 1 展示了在同一个持久化上下文中,具有相同 ID 和类的两个被管理的实体,返回的是同一个实例。
清单 1. 同一持久化上下文中的实体操作
@Stateless public Student implements Person {
@PersistenceContext EntityManager entityManager;
public OrderLine createStudnet(String id, String name) {
Student student1 = new Student (“0001”, “Tom”);
entityManager.persist(student1); //Managed
Student student2 =entityManager.find(Student, student1.getId()));
(student1 == student2) // TRUE
return (student);
}
}
JPA 二级缓存
JPA 二级缓存是跨持久化上下文共享实体状态的,是真正意义上的全局应用缓存。如果二级缓存激活,JPA 会先从一级缓存寻找实体,未找到再从二级缓存中寻找。JPA 1.0 目前不支持二级缓存,但大部分的 persistence provider 提供对二级缓存的支持。JPA 2.0 规范提供一些基本缓存操作的 API,可以在 EntityManagerFactory 中使用:
清单 2. JPA 2.0 提供的新 API
public class Cache {
// 检查对象是否在 Map 中
public Boolean contains(Class class, Object pk);
// 失效 Map 中的对象
public void evict(Class class, Object pk)
// 失效 Map 中的类
public void evict(Class class)
// 失效所有在 Map 中的类
public void evictAll();
}
图 2. JPA 二级缓存

当二级缓存有效时,你就不能依靠事务来保护并发数据的,而是依靠锁策略:
- 在确认修改后,需要手工处理乐观锁失败
- 配置失效期,以及刷新策略,最小化使用锁策略
二级高速缓存通常是用来提高性能。但是,使用缓存可能会导致提取“陈旧”数据,表 1 展示了 JPA 的二级缓存优点和缺点:
表 1. JPA 的二级缓存优点和缺点
优点 |
缺点 |
避免了已经加载对象的数据库访问
对于频繁访问的未修改的对象读取更快 |
内存消耗较大
会出现“陈旧”数据
会出现并发写的情况 |
所以二级缓存最好是用在经常阅读经常和修改的数据,而不是至关重要的数据。
下图展示了在使用了二级缓存之后,随着用户数目的增加,每秒钟的可操作次数将比不使用二级缓存的情况好很多。
图 3.使用二级缓存和不使用二级缓存对比
OpenJPA 中的缓存机制
Apache OpenJPA 是业界领先的开源 Java 持久化框架。WebSphere Application Server 的 JPA 实现是基于 Apache OpenJPA 的。WebSphere Application Serve r 在 V6.1 版本的 EJB 3.0 功能部件包中支持 JPA 1.0 规范。WebSphere Application Server V7.0 中支持 JPA 1.2。WebSphere Application Server V7.0 的 JPA 2.0 功能部件包基于 OpenJPA 2.0,提供了 IBM 对 JPA 2.0 规范的实现,并增加 IBM 的增强特性,使得其能与 WebSphere Application Server 更好地集成。WebSphere Application Server 对 JPA 的支持是向前兼容的,也就是说 JPA 2.0 功能部件包同时也支持基于 JPA 1.0 和 JPA 1.2 规范开发的应用。
图 4.WAS 和 OpenJPA 发布时间表

因为基于 OpenJPA,所以基于 OpenJPA 应用不需要做任何更改就可以运行在 WebSphere Application Server 上。此外,WebSphere Application Server 支持其与 IBM 已有特性更好地集成,包括事务、安全、集群等。同时 JPA 2.0 功能部件包还包括对二级缓存的支持。
OpenJPA 的缓存机制要比其他的 ORM 框架自带的缓存要强大的多,其他的 ORM 框架如 hibernate 自带的缓存是 Session 级别的,如果你想实现跨 Session 的缓存就必须集成第三方的缓存 (Ehcache, oscache), 但 OpenJPA 的缓存是能够跨 Session 的,对应 JPA 的标准来说就是跨 entityManager 是 EntityManagerFactory 级别的。OpenJPA 的缓存还能够支持多 JVM,也就是能够在分布式的环境中使用。
OpenJPA 的缓存分为两种,一种是 DataCache 用来缓存 EntityManagerFactory 级别的持久化实体的。当启用 DataCache 后,OpenJPA 会先检查 DataCache,如果实体不存在再从存储介质中读取。使用时需要加入以下配置文件到应用的 persistence.xml 中的 <properties> 下
清单 3. OpenJPA 的 DataCache
<property name="openjpa.DataCache" value="true(CacheSize=5000, SoftReferenceSize=0)" />
<property name="openjpa.RemoteCommitProvider" value="sjvm" />
其中 CacheSize 指的是缓存实体的最大数目,默认 CacheSize 是 1000,如果缓存实体超出这一数值时,OpenJPA 会随机逐出一些实体,直至实体数目小于设定的最大数目。这里需要注意的是 SoftReferenceSize 这个属性,SofeReference 是指 OpenJPA 的缓存如果达到最大存储数目,或根据一定的算法需要从缓存中移出一下数据时,会把它们放到一个 softReferenceMap 中,softReferenceMap 中的数据也会删除只是这些数据比直接删除会待的时间久一些,以防止在缓存删除他们以后程序马上就访问他们。这样下次访问到这些移出的数据时就会到 softReferenceMap 中去找,这样比直接删除和重新访问数据库查找的效率好一点。这点跟其他的缓存实现有点不一样,其它的缓存是直接从缓存中删除这些数据。 如果你不想使用 sofeReferenceMap, 就把它的值设成 0。
第二种缓存是 QueryCache,OpenJPA 默认在你启用 DataCache 后,自动帮你启用 QueryCache。QueryCache 主要用来缓存查询语句返回的结果,设置如下:
清单 4. OpenJPA 的 QueryCache
<property name="openjpa.QueryCache" value="CacheSize=1000, SoftReferenceSize=100"/>
WAS JPA 2.0 功能部件包
接下来将向大家展示如何在 WAS7 中实现对 JPA 2.0 和二级缓存的支持,首先需要安装 OSGi&JPA 2.0 功能部件包。
下载 IBM 的安装管理器和 OSGi&JPA 2.0 功能部件包:
IBM 安装管理器 下载地址:
http://www-01.ibm.com/support/docview.wss?rs=180&uid=swg24023498
OSGi&JPA 2.0 功能部件包 下载地址:
http://www-01.ibm.com/software/webservers/appserv/was/featurepacks/osgi/index.html
安装好 WAS 7 和安装管理器之后,解压缩 OSGi&JPA 2.0 功能部件包。下图为安装管理器的管理界面:
图 5. 安装管理器界面
- 点击文件 -> 首选项,导入 OSGi&JPA 2.0 功能部件包到安装管理器的存储库中:
图 6. 导入存储库

- 点击导入,导入 WAS7 的安装路径:
图 7. 导入 WAS 7 所在的路径

- 点击安装,安装 OSGi&JPA 2.0 功能部件包:
图 8. 安装 OSGi&JPA 2.0 功能部件包

- 功能部件包安装成功之后,我们就可以通过概要管理工具创建带 OSGi 或者 JPA 2.0 特性的概要文件,也可以对原有的概要文件进行升级。
图 9. WAS7 概要管理工具

创建完带 JPA 2.0 功能部件的应用程序服务器之后,此应用程序服务器就可以支持 OpenJPA 2.0 及 JPA 二级缓存了。
在 WAS7 中我们可以使用 OpenJPA 自带的默认 Cache Provider 实现 JPA 的二级缓存,也可以安装其他的 Cache Provider 来实现二级缓存,比如 WebSphere eXtreme Scale。配置方式和 OpenJPA 默认方式类似,不同之处是利用 eXtreme Scale 自带的 API 实现 Cache Provider,并设置相应的参数,配置如下:
清单 5. WebSphere eXtreme Scale 二级缓存配置
<property name="openjpa.DataCache"
value="com.ibm.websphere.objectgrid.openjpa
.ObjectGridDataCache(objectGridName=TRADER,
objectGridType=EMBEDDED_PARTITION,
maxNumberOfReplicas=1,NumberOfPartitions=2)"/>
结束语
随着 WAS7 OSGi&JPA 2.0 功能部件包的推出,WAS7 已经全面支持 OpenJPA 2.0 和 JPA 二级缓存。如果在应用中存在大量经常读取但不常修改的数据,可以利用二级缓存在持久化上下文中共享实体的状态从而提高数据访问的效率。