📄 openjpa.htm
字号:
<TH class=thleft noWrap width=150 height=26>发表人</TH>
<TH class=thright noWrap width="100%">内容</TH></TR><!-- POST LISTING -->
<TR>
<TD class=postInfo colSpan=2>
<TABLE width="100%">
<TBODY>
<TR>
<TD><SPAN class=gen><A
href="http://bbs.163jsp.com/forums/show/1.html;jsessionid=95B9B6452ADB2E164F4DC77CC375B6F5">JAVA技术</A>
» <A
href="http://bbs.163jsp.com/posts/list/777.html;jsessionid=95B9B6452ADB2E164F4DC77CC375B6F5">OpenJPA项目</A>
» <A
href="http://bbs.163jsp.com/posts/preList/777/1529.html;jsessionid=95B9B6452ADB2E164F4DC77CC375B6F5#1529">加入讨论</A>
</SPAN></TD>
<TD align=right>
<SCRIPT
type=text/javascript>writeStars(0, 1529);</SCRIPT>
</TD></TR></TBODY></TABLE></TD></TR>
<TR><!-- Message -->
<TD class=row1 vAlign=top colSpan=2><SPAN class=postbody><FONT
color=red><SPAN
style="FONT-SIZE: 18px; LINE-HEIGHT: normal"><B>第 6 部分:
处理实体生命周期事件的回调</B></SPAN></FONT> <BR>JPA(Java Persistence API)是
EJB 3.0 新引入的数据持久化编程模型。JPA
充分利用了注释(Annotation)和对象/关系映射,为数据持久化提供了更简单、易用的编程方式。OpenJPA 是
Apache 组织提供的 JPA 标准实现。本文是 使用 Apache OpenJPA 开发 EJB 3.0 应用系列
的第六部分,介绍了 OpenJPA
中对实体生命周期过程中的回调的支持,包括使用回调方法和监听器实现回调,并且通过简单的例子描述了 OpenJPA
容器中实体回调的实现过程。
<BR><BR>企业应用开发过程中,经常会存在这样的需求:当企业应用中的某些数据被增加、删除、修改时,引发一些特定的动作,完成企业应用中的一些特别的要求,比如企业应用中要完成数据操作日志、处理数据之间的某种关系、或者是完成一些局部的统计工作等。通常情况下,开发者有两种选择:
<BR><BR>1. 开发者提供独立的代码来处理这种需求; <BR>2. 使用关系型数据库中的“触发器”技术 ,
让开发者指定在特定表中添加、删除、修改数据时引发特定的动作,完成数据库中数据的处理。
<BR><BR>然而这两种方式都有一定的局限性,在第 1
种方式中,特别设计的代码和主体程序之间的耦合性较高,无法独立维护,很难复用;第 2
种方式仅仅适用于关系型数据库开发的情况,开发方式比较受局限。 <BR><BR>OpenJPA
中提供了另外一种方式来处理这种特殊的需求,即回调方法。回调方法可以监视实体的整个生命周期,在生命周期的各个时期均可以轻松的加入开发者自己的代码,处理实际业务中的特殊需求。OpenJPA
中目前支持的实体生命周期包括:实体持久化之前、实体可以被持久化、实体被加载之后、实体状态写入数据库之前、实体状态写入数据库之后、实体被删除之前、实体被删除之后。
<BR><BR>OpenJPA 中的回调方法可以在两个层次上实现 : <BR><BR>1. 在实体类中定义回调方法
<BR><BR>开发者在实体类中编写与实际业务需求相匹配的处理方法,通过注释将这些方法注册到实体生命周期监听机制中,当实体的对应生命周期事件触发时,这些方法将被调用,从而满足用户的特定业务需求。这种方式适用于那些回调方法不太多、业务也不复杂的情况,同时这也不是被推荐的一种编程方式。
<BR>2. 为实体类提供监听器
<BR><BR>开发者除了在实体类中定义回调方法之外,还有一种方式可以将实体的生命周期事件和 Java
方法联系起来,就是使用实体监听器,它使用类似 Awt 或者 Swing
中的监听机制。开发者提供实体监听器,然后将这些监听器注册到合适的实体上,实体成为事件发生的源。当实体生命周期事件触发时,这些被注册的实体监听器将会逐一被激活。使用实体监听器,可以实现监听器的继承、共享、复用,因此能够适用于比简单使用回调方法更复杂的业务环境下。
<BR><BR><SPAN
style="FONT-SIZE: 18px; LINE-HEIGHT: normal"><B>实体生命周期相关注释</B></SPAN>
<BR><BR>OpenJPA
中能够为实体生命周期的多个阶段提供回调支持,包括实体持久化之前、持久化时、被初始化时等。实体生命周期的每一个阶段在 JPA
中都有相应的回调方法注释,这些注释可以在实体类或者实体类的监听器中使用,开发者使用这些注释来指派回调发生时实体类中被调用的方法。
<BR><BR>OpenJPA 中支持的实体生命周期和它们对应的注释如下 : <BR>属性 说明
<BR><B>javax.persistence.PrePersist</B>
<BR><BR>使用该注释的方法将在实体被持久化之前被调用。 <BR><BR>被 PrePersist
注释的方法中通常为实体的一些属性提供某种特殊值或者完成某些计算任务,比如开发者可以在 PrePersist
注释的方法中设置实体对象的主键值或者对一些持久化字段的内容进行计算。
<BR><B>javax.persistence.PostPersist </B><BR><BR>使用
PostPersist 注释的方法在实体设置为可持久化时被调用。 <BR><BR>被 PostPersist
注释的方法中通常完成实体持久化后的一些后续动作,比如在常见的 MVC 模式下,开发者在实体被持久化完成后,使用被
PostPersist 注释的方法完成视图层的更新,另外一种常见的处理是去完成一些额外的数据一致性处理。
<BR><B>javax.persistence.PostLoad </B><BR><BR>使用 PostLoad
注释的方法在实体的所有提前抓取字段从数据库中完全取出时被调用。 <BR><BR>在被 PostLoad 注释的方法中 ,
无法访问到延迟抓取的持久字段的值。在 OpenJPA
中,实体的属性支持提前抓取或者延迟抓取两种策略,提前抓取是指实体属性在实体查询 SQL
执行时就已经从数据库中提取到内存中,延迟抓取是指实体属性一些字段在实体查询 SQL
执行时并没有从数据库中提取到内存中,而是在应用访问到该字段时才从数据库中,延迟抓取主要适用于一些比较大的对象如大字符对象。
<BR><BR>被 PostLoad 注释的方法中通常的业务逻辑就是初始化非持久字段 ,
这些非持久化字段的值依赖于实体的其他持久字段的值,比如企业应用中要显示一个用户的图片时,由于图片通常保存在文件系统中,就需要在被
PostLoad 注释的方法中根据用户的信息初始化图片信息。
<BR><B>javax.persistence.PreUpdate </B><BR><BR>使用 PreUpdate
注释的方法在对象状态被保存到数据库中之前被调用。 <BR><BR>被 PreUpdate 方法注释的方法的业务逻辑通常和被
PostLoad 方法注释的方法中的业务逻辑正好相反。被 PostLoad
注释的方法中使用持久化数据初始化非持久化字段的内容 , 被 PreUpdate
注释的方法中则通常用非持久化数据的内容设置持久化字段的值。
<BR><B>javax.persistence.PostUpdate </B><BR><BR>使用 PostUpdate
注释的方法在对象状态保存到数据库后调用。 <BR><BR>使用 PostUpdate
注释的方法中处理的业务逻辑一般作用是清除应用层缓存的、过期的数据,避免它们造成对企业应用性能的影响。
<BR><B>javax.persistence.PreRemove </B><BR><BR>使用 PreRemove
注释的方法在对象被删除的时候调用。 <BR><BR>在被 PreRemove
注释的方法中访问持久字段是不支持的,可以使用该方法实现级连删除 , 或者完成其他清除策略。
<BR><B>javax.persistence.PostRemove</B> <BR><BR>使用 PostRemove
注释的方法在实体对象被删除后调用。 <BR><BR><SPAN
style="FONT-SIZE: 18px; LINE-HEIGHT: normal"><B>如何使用回调方法</B></SPAN>
<BR><BR>上面我们学习了将 Java
代码和实体事件结合起来的一些注释和它们的适用情况,下面我们学习如何在企业应用中使用这些注释从而实现实体生命周期事件的回调。
<BR><BR>首先我们学习如何使用回调方法来处理实体生命周期事件的回调,回调方法都是在实体内中直接定义的 Java
方法,这些回调方法可以是任何没有参数的方法。OpenJPA 中,一个 Java
方法可以同时支持多个注释,这样它可以处理实体生命周期中多个阶段的回调,比如我们在实体中定义一个 Java 方法
printHistory,我们可以同时使用 javax.persistence.PrePersist 和
javax.persistence.PostPersist 注释它,这样 printHistory
方法在实体被持久化之前和之后都会被调用。
<BR><BR>下面我们结合简单的例子来解释如何使用回调方法处理实体生命周期事件的回调,假设存在这样的业务需求,对于实体
Animal,它有两个属性 id 和 name,我们需要在企业应用运行中跟踪 Animal
全部生命周期过程中的状态变化,并且将这种变化的过程打印在控制台上。 <BR><BR>我们需要为实体类额外定义 7 个
Java 方法,他们分别处理实体生命周期的 7 个事件,然后通过上一节中提到的 7
个注释将它们和实体生命周期联系起来,完整的 Animal 实体类的代码如下: <BR><BR>清单 1. Animal
实体类的代码 <BR><BR><BR>1. public class Animal { <BR>2. package
org.vivianj.openjpa.beans; <BR>3. <BR>4. import
javax.persistence.Entity; <BR>5. import javax.persistence.Id;
<BR>6. import javax.persistence.PostLoad; <BR>7. import
javax.persistence.PostPersist; <BR>8. import
javax.persistence.PostRemove; <BR>9. import
javax.persistence.PostUpdate; <BR>10. import
javax.persistence.PrePersist; <BR>11. import
javax.persistence.PreRemove; <BR>12. import
javax.persistence.PreUpdate; <BR>13. <BR>14. @Entity <BR>15.
public class Animal { <BR>16. @Id <BR>17. private long id;
<BR>18. <BR>19. private String name; <BR>20. <BR>21. public
long getId() { <BR>22. return id; <BR>23. } <BR>24. <BR>25.
public void setId(long id) { <BR>26. this.id = id; <BR>27. }
<BR>28. <BR>29. public String getName() { <BR>30. return name;
<BR>31. } <BR>32. <BR>33. public void setName(String name) {
<BR>34. this.name = name; <BR>35. } <BR>36. <BR>37. /**
<BR>38. * logPrePersist 方法处理实体生命周期中的 PrePersist[实体被持久化之前]事件
<BR>39. */ <BR>40. @PrePersist <BR>41. public void
logPrePersist() { <BR>42. System.out.println("Animal[" + id +
"," + name + "] 将被持久化到数据库中。"); <BR>43. } <BR>44. <BR>45. /**
<BR>46. * logPostPersist方法处理实体生命周期中的PostPersist[实体可以被持久化]事件
<BR>47. */ <BR>48. @PostPersist <BR>49. public void
logPostPersist() { <BR>50. System.out.println("Animal[" + id +
"," + name + "] 可以被持久化到数据库中了。"); <BR>51. } <BR>52. <BR>53. /**
<BR>54. * logPostLoad方法处理实体生命周期中的PostLoad[实体被加载到之后]事件 <BR>55.
*/ <BR>56. @PostLoad <BR>57. public void logPostLoad() {
<BR>58. System.out.println("Animal[" + id + "," + name + "]
已经加载到内存中。"); <BR>59. } <BR>60. <BR>61. /** <BR>62. *
logPreUpdate方法处理实体生命周期中的PreUpdate[实体状态写入数据库之前]事件 <BR>63. */
<BR>64. @PreUpdate <BR>65. public void logPreUpdate() {
<BR>66. System.out.println("Animal[" + id + "," + name + "]
将很快被持久化到数据库中。"); <BR>67. } <BR>68. <BR>69. /** <BR>70. *
logPostUpdate方法处理实体生命周期中的PostUpdate[实体状态写入数据库之后]事件 <BR>71. */
<BR>72. @PostUpdate <BR>73. public void logPostUpdate() {
<BR>74. System.out.println("Animal[" + id + "," + name + "]
已经被持久化到数据库中。"); <BR>75. } <BR>76. <BR>77. /** <BR>78. *
logPreRemove方法处理实体生命周期中的PreRemove[实体被删除之前]事件 <BR>79. */
<BR>80. @PreRemove <BR>81. public void logPreRemove() {
<BR>82. System.out.println("Animal[" + id + "," + name + "]
将从数据库中删除。"); <BR>83. } <BR>84. <BR>85. /** <BR>86. *
logPostRemove 方法处理实体生命周期中的 PostRemove [实体被删除之后]事件 <BR>87. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -