JPA(Java Persistence API)是 EJB 3.0 新引入的数据持久化编程模型。JPA 充分利用了注释(Annotation)和对象/关系映射,为数据持久化提供了更简单、易用的编程方式。OpenJPA 是 Apache 组织提供的 JPA 标准实现。本文是 使用 Apache OpenJPA 开发 EJB 3.0 应用系列 的第六部分,介绍了 OpenJPA 中对实体生命周期过程中的回调的支持,包括使用回调方法和监听器实现回调,并且通过简单的例子描述了 OpenJPA 容器中实体回调的实现过程。
企业应用开发过程 中,经常会存在这样的需求:当企业应用中的某些数据被增加、删除、修改时,引发一些特定的动作,完成企业应用中的一些特别的要求,比如企业应用中要完成数 据操作日志、处理数据之间的某种关系、或者是完成一些局部的统计工作等。通常情况下,开发者有两种选择:
- 开发者提供独立的代码来处理这种需求;
- 使用关系型数据库中的“触发器”技术 , 让开发者指定在特定表中添加、删除、修改数据时引发特定的动作,完成数据库中数据的处理。
然而这两种方式都有一定的局限性,在第 1 种方式中,特别设计的代码和主体程序之间的耦合性较高,无法独立维护,很难复用;第 2 种方式仅仅适用于关系型数据库开发的情况,开发方式比较受局限。
OpenJPA 中提供了另外一种方式来处理这种特殊的需求,即回调方法。回调方法可以监视实体的整个生命周期,在生命周期的各个时期均可以轻松的加入开发者自己的代码, 处理实际业务中的特殊需求。OpenJPA 中目前支持的实体生命周期包括:实体持久化之前、实体可以被持久化、实体被加载之后、实体状态写入数据库之前、实体状态写入数据库之后、实体被删除之前、 实体被删除之后。
OpenJPA 中的回调方法可以在两个层次上实现 :
- 在实体类中定义回调方法
开发者在实体类中编写与实际业务需求相匹配的处理方法,通过注释将这些方法注册到实体生命周期监听机制中,当实体的对应生命周期事件触发时,这些方 法将被调用,从而满足用户的特定业务需求。这种方式适用于那些回调方法不太多、业务也不复杂的情况,同时这也不是被推荐的一种编程方式。
- 为实体类提供监听器
开发者除了在实体类中定义回调方法之外,还有一种方式可以将实体的生命周期事件和 Java 方法联系起来,就是使用实体监听器,它使用类似 Awt 或者 Swing 中的监听机制。开发者提供实体监听器,然后将这些监听器注册到合适的实体上,实体成为事件发生的源。当实体生命周期事件触发时,这些被注册的实体监听器将 会逐一被激活。使用实体监听器,可以实现监听器的继承、共享、复用,因此能够适用于比简单使用回调方法更复杂的业务环境下。
OpenJPA 中能够为实体生命周期的多个阶段提供回调支持,包括实体持久化之前、持久化时、被初始化时等。实体生命周期的每一个阶段在 JPA 中都有相应的回调方法注释,这些注释可以在实体类或者实体类的监听器中使用,开发者使用这些注释来指派回调发生时实体类中被调用的方法。
OpenJPA 中支持的实体生命周期和它们对应的注释如下 :
属性 | 说明 |
---|---|
javax.persistence.PrePersist | 使用该注释的方法将在实体被持久化之前被调用。 被 |
javax.persistence.PostPersist | 使用 被 |
javax.persistence.PostLoad | 使用 在被 被 |
javax.persistence.PreUpdate | 使用 被 |
javax.persistence.PostUpdate | 使用 使用 |
javax.persistence.PreRemove | 使用 在被 |
javax.persistence.PostRemove | 使用 |
![]() ![]() |
![]()
|
上面我们学习了将 Java 代码和实体事件结合起来的一些注释和它们的适用情况,下面我们学习如何在企业应用中使用这些注释从而实现实体生命周期事件的回调。
首 先我们学习如何使用回调方法来处理实体生命周期事件的回调,回调方法都是在实体内中直接定义的 Java 方法,这些回调方法可以是任何没有参数的方法。OpenJPA 中,一个 Java 方法可以同时支持多个注释,这样它可以处理实体生命周期中多个阶段的回调,比如我们在实体中定义一个 Java 方法 printHistory
,我们可以同时使用 javax.persistence.PrePersist
和 javax.persistence.PostPersist
注释它,这样 printHistory
方法在实体被持久化之前和之后都会被调用。
下面我们结合简单的例子来解释如何使用回调方法处理实体生命周期事件的回调,假设存在这样的业务需求,对于实体 Animal
,它有两个属性 id
和 name
,我们需要在企业应用运行中跟踪 Animal
全部生命周期过程中的状态变化,并且将这种变化的过程打印在控制台上。
我们需要为实体类额外定义 7 个 Java 方法,他们分别处理实体生命周期的 7 个事件,然后通过上一节中提到的 7 个注释将它们和实体生命周期联系起来,完整的 Animal
实体类的代码如下:
清单 1. Animal 实体类的代码
|
我们可以使用下面的客户端代码完成实体的增加、查找、修改、删除工作:
清单 2. 实现实体的增加、查找、修改、删除的代码
|
下面的信息是执行上面的客户端后控制台打印出的信息,通过这些信息的先后顺序,我们可以了解到这些事件的具体时机和先后顺序:
清单 3. 客户端后控制台打印出的信息
|
OpenJPA 中还可以将一个 Java 方法注册到两个实体生命周期事件上,比如我们可以用下面的这段代码,将 Animal
实体 log
方法注册到 PrePersist
和 PostPersiste
这两个实体生命周期事件上。
清单 4. 将方法注册到两个实体生命周期事件上
|
![]() ![]() |
![]()
|
在实体类中同时提供处理实体生命周期回调方法的代码不是很优雅的编程方式,开发者通常考虑使用非持久的监听器类处理回调方法。OpenJPA 中支持使用实体监听器处理实体的回调方法 , 而不是直接在实体类中处理回调方法。
在 OpenJPA 中,实体监听器类需要提供一个 public 的无参数构造器,其他要求和在实体类中定义回调方法一样 , 一个监听器类同样可以处理多种回调,只需要为监听器中的方法提供回调方法对应的注释如 javax.persistence.PrePersist
、javax.persistence.PostPersist
等。特别的是,监听器中的每一个回调方法必须有一个 java.lang.Object
类型的参数,该参数对应的对象代表了触发当前事件的实体对象。
我们可以使用下面的代码创建一个实体监听器类。
清单 5. 创建一个实体监听器类
|
创建实体监听器后,开发者将实体监听器注册到需要被监听的实体中,使用 javax.persistence.EntityListeners
注释可以为实体注册监听器,这个注释支持同时为实体类设置多个监听器 , 只需要在注释的属性中提供多个参数,各参数之间使用”,”隔开。我们可以使用下面的代码为实体注册一个或者多个监听器类。
清单 6. 为实体注册一个或者多个监听器类
|
![]() ![]() |
![]()
|
由于 OpenJPA 中实体是支持继承的,实体之间的监听器也被实体的子类继承下来,这些实体监听器方法在被触发时的遵循下面的调用顺序:
- 首先,默认的监听器首先被调用,默认的监听器是指在包注释中定义的监听器;
- 接下来 , 实体监听器按照继承层次顺序被调用 , 父类监听器在子类监听器之前被调用;
- 最后 , 如果一个实体的同一个回调事件要触发多个监听器的话 , 这些监听器按照声明的先后顺序被调用;
开发者可以选择屏蔽在父类或者包中声明的监听器,只需要使用下面两个类级别的注释 :
-
javax.persistence.ExcludeSuperclassListeners
为实体类提供javax.persistence.ExcludeSuperclassListeners
注释,可以屏蔽所有当前实体类的所有父类中声明的实体监听器。 -
javax.persistence.ExcludeDefaultListeners
为实体类提供javax.persistence.ExcludeDefaultListeners
注释,可以屏蔽当前实体类和它所有子类的所有默认监听器。
![]() ![]() |
![]()
|
企 业应用中经常有一些特别的需求:在某一个数据被处理的时候,需要引发一连串的操作,OpenJPA 中提供实体生命周期事件回调机制为这种需求提供了更好的解决方案,OpenJPA 中实体生命周期能够支持实体被持久化之前、实体可以被持久化、实体状态写入数据库之前、实体状态写入数据库之后、实体被加载、实体被删除之前、实体被删除 之后共 7 种事件,开发者可以根据需要选择为其中的一个或者多个事件编写回调方法。本文中结合简单的例子描述了如何通过 OpenJPA 提供简单的注释、结合 Java 方法就可以监听、处理实体生命周期事件回调的过程。
![]() ![]() |
![]()
|
描述 | 名字 | 大小 | 下载方法 |
---|---|---|---|
本文样例代码 | OpenJPA_Callback_Examples.zip | 6 KB | HTTP |
![]() | ||||
![]() | 关于下载方法的信息 | ![]() |
学习
- " 使用 Apache OpenJPA 开发 EJB 3.0 应用系列 ":查看本系列完整的文章列表。
- "通过 WebSphere Application Server V6.1 利用 OpenJPA"(developerWorks,2007 年 2 月 25 日):通过一个完整的示例来介绍 OpenJPA,并演示了如何通过 IBM® WebSphere® Application Server V6.1 来利用 OpenJPA 的部分功能。
- "Spring 2 和 JPA 简介"(developerWorks,2006 年 8 月 28 日):通过这份教程,您将学习如何用 Spring 2 框架和 JPA 从头开始创建服务器应用程序。
- "使用 EJB 3.0 Java Persistence API 设计应用程序程序"(developerWorks,2006 年 5 月 11 日):研究一个利用 JPA 验证 Java EE 设计模式并演示关键设计决策的示例应用程序。
- EJB 3.0 规范:在 JCP 网站上查看 EJB 3.0 规范的标准文档。
- OpenJPA 在线文档:在 OpenJPA 官方网站上查看 OpenJPA 相关的技术文档。
- Java 技术专区:数百篇关于 Java 编程各方面的文章。
- 开源软件技术专区:获得丰富的 how-to 信息、工具和项目更新,帮助您用开放源码技术进行开发,并与 IBM 产品结合使用。
获得产品和技术
- Apache OpenJPA 官方网站:获取 OpenJPA 项目的最新发布包和源代码。
- Sun 公司 Java 站点:获取 JDK 最新的安装包。
- MySQL 网站:获取最新的 MySQL 数据库。
![]() | ||
![]() | 肖 菁是自由撰稿人,致力于研究和推广 Java EE、Open Source、SOA 技术,发起了开源项目 BuildFileDesigner(buildfiledesign.sourceforge.net) 和 V-Security(v-security.sourceforge.net)。可以通过电子邮件 cto4u.cn@gmail.com 和他取得联系。 |