6.3 控制实体Bean的生命周期
与会话Bean类似,实体Bean也有自己的生命周期,分别对应不同的状态。下面我们首先来讲解实体Bean的状态和生命周期事件。
l 实体Bean的状态:新实体、持久化实体、分离实体、删除实体。
l 实体Bean的事件:@PostLoad、@PrePersist和@PostPersist、@PreUpdate和@PostUpdate、@PreRemove和@PostRemove。
然后讲解进行事件回调的两种方法。
l 外部回调:编写外部监听器。
l 内部回调:编写内部监听函数。
<DIV> </DIV> |
6.3.1 实体Bean生命周期的4种状态
在上一节中我们讲解了使用实体管理器的各种函数操作数据库的方法,其中有两个方法与实体是否被容器管理有关:
l contains()用来检查实体是否被管理。
l clear()分离实体。
从以上的概念我们可以了解到,实体分为被容器管理和不被容器管理两种,这代表了实体的两种存在状态。
实际上,实体共有4种状态。
l new——新实体:实体由应用产生,和实体管理器没有任何联系,也没有唯一的标识符。
l managed——持久化实体:新实体和实体管理器产生关联(通过persist()、merge()等方法),在实体管理器中存在和被管理,标志是在实体管理器中有一个唯一的标识符。
l detached——分离的实体:实体有唯一的标识符,但它的标识符不被实体管理器管理。
l removed——删除的实体:实体被remove()方法删除,对应的记录将会在当前事务提交的时候从数据库中删除。
<DIV> </DIV> |
6.3.2 实体Bean生命周期的回调事件
当你在执行各种持久化方法的时候,实体的状态会随之改变,状态的改变会引发不同的生命周期事件。这些事件可以使用不同的注释符来指示发生时的回调函数。
l @javax.persistence.PostLoad:加载后。
l @javax.persistence.PrePersist:持久化前。
l @javax.persistence.PostPersist:持久化后。
l @javax.persistence.PreUpdate:更新前。
l @javax.persistence.PostUpdate:更新后。
l @javax.persistence.PreRemove:删除前。
l @javax.persistence.PostRemove:删除后。
图6-3演示了回调事件和各种状态之间的转换关系。
以上的7种事件对应了数据库的4种操作。
1)数据库查询
@PostLoad事件在下列情况下触发:
l 执行EntityManager.find()或getreference()方法载入一个实体后。
l 执行JPQL查询后。
l EntityManager.refresh()方法被调用后。
2)数据库插入
@PrePersist和@PostPersist事件在实体对象插入到数据库的过程中发生:
l @PrePersist事件在调用persist()方法后立刻发生,此时的数据还没有真正插入进数据库。
l @PostPersist事件在数据已经插入进数据库后发生。
3)数据库更新
@PreUpdate和@PostUpdate事件的触发由更新实体引起:
l @PreUpdate事件在实体的状态同步到数据库之前触发,此时的数据还没有真正更新到数据库。
l @PostUpdate事件在实体的状态同步到数据库之后触发,同步在事务提交时发生。
4)数据库删除
@PreRemove和@PostRemove事件的触发由删除实体引起:
l @PreRemove事件在实体从数据库删除之前触发,即在调用remove()方法删除时发生,此时的数据还没有真正从数据库中删除。
l @PostRemove事件在实体从数据库中删除后触发。
<DIV> </DIV> |
6.3.3 外部回调——编写外部监听器
对于以上7种事件类型,我们可以用两种方法来实现回调。
l 外部回调:即编写一个实体监听器,实现对某一个实体Bean的回调。
l 内部回调:通过在实体Bean内部的监听进行回调。
首先来看看外部回调的方法。
外部回调需要首先编写一个回调的监听器类,该类需要使用@javax.persistence.EntityListeners注释符绑定目标实体Bean,实现实体Bean的监听。
在该类中可以编写多个回调方法,通过在方法前添加不同的回调注释符实现事件的拦截。回调方法需要返回void型值,并且拥有一个Object参数,形式如下:
void method(Object obj);
下例定义了一个监听器类MyEntityListener.java,代码如下所示:
程序6-6 外部监听器类MyEntityListener.java
package com.ejb.listener;
import javax.persistence.PostLoad;
import javax.persistence.PostPersist;
import javax.persistence.PostRemove;
import javax.persistence.PostUpdate;
import javax.persistence.PrePersist;
import javax.persistence.PreRemove;
import javax.persistence.PreUpdate;
public class MyEntityListener {
@PostLoad
public void postLoad(Object obj) {
System.out.println("载入了实体Bean:" + obj.getClass().getName());
}
@PrePersist
public void PreInsert(Object obj) {
System.out.println("持久化实体Bean前:" + obj.getClass().getName());
}
@PostPersist
public void postInsert(Object obj) {
System.out.println("持久化实体Bean后:" + obj.getClass().getName());
}
@PreUpdate
public void PreUpdate(Object obj) {
System.out.println("更新实体Bean前:" + obj.getClass().getName());
}
@PostUpdate
public void PostUpdate(Object obj) {
System.out.println("更新实体Bean后:" + obj.getClass().getName());
}
@PreRemove
public void PreRemove(Object obj) {
System.out.println("删除实体Bean前:" + obj.getClass().getName());
}
@PostRemove
public void PostRemove(Object obj) {
System.out.println("删除实体Bean后:" + obj.getClass().getName());
}
}
然后就可以在实体Bean类中通过@javax.persistence.EntityListeners注释来引用该监听器了,例如我们为Student.java类添加如下引用:
@EntityListeners({MyEntityListener.class})
public class Student implements Serializable {
...
}
现在重新发布EntityBeanTest.jar到JBoss服务器下,就会加载该监听器。此时再运行测试类StudentDAOClient.java,就会在JBoss控制台中输出如下信息:
14:07:39,843 INFO [STDOUT] 持久化实体Bean前:com.ejb.entitybean.Student
14:07:40,093 INFO [STDOUT] 持久化实体Bean后:com.ejb.entitybean.Student
这就表明,在执行数据插入前后分别回调了监听器PreInsert()和PostInsert()函数。
<DIV style="PADDING-BOTTOM: 0pt; PADDING-LEFT: 0pt; PADDING-RIGHT: 0pt; PADDING-TOP: 0pt" class=shape v:shape="_x0000_s1026"> 提示 </DIV> |
这种通过编写外部监听器的方法,类似于AOP的功能,可以通过外部类实现对系统中目标类和函数的监听。关于AOP详细知识请参见作者的《开发者突击:精通AOP(AspectJ+ AspectWerkz+SpringAOP)》一书。
<DIV> </DIV> |
6.3.4 内部回调——编写内部监听函数
除了外部类可以实现生命周期事件的回调外,你也可以把回调方法写在实体Bean中。要注意,直接写在实体Bean中的回调方法不需带任何参数,格式如下:
void method();
不同的回调函数也是通过回调注释符进行注释来标注的。如下例所示,我们为Student.java添加了回调函数:
程序6-7 内部监听演示Student.java
package com.ejb.entitybean;
import java.io.Serializable;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EntityListeners;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import com.ejb.MyEntityListener;
@SuppressWarnings("serial")
@Entity
@Table(name = "Student")
@EntityListeners({MyEntityListener.class})
public class Student implements Serializable {
private Integer studentid; //学号
private String name; //姓名
private boolean sex; //性别
private Short age; //年龄
private Date birthday; //出生日期
private String address; //地址
private String telephone; //电话
...
package com.ejb.entitybean;
import java.io.Serializable;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EntityListeners;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import com.ejb.MyEntityListener;
@SuppressWarnings("serial")
@Entity
@Table(name = "Student")
@EntityListeners({MyEntityListener.class})
public class Student implements Serializable {
private Integer studentid; //学号
private String name; //姓名
private boolean sex; //性别
private Short age; //年龄
private Date birthday; //出生日期
private String address; //地址
private String telephone; //电话
...
@PostLoad
public void postLoad(Object obj) {
System.out.println("载入了实体Bean:" + obj.getClass().getName());
}
@PrePersist
public void PreInsert(Object obj) {
System.out.println("持久化实体Bean前:" + obj.getClass().getName());
}
@PostPersist
public void postInsert(Object obj) {
System.out.println("持久化实体Bean后:" + obj.getClass().getName());
}
@PreUpdate
public void PreUpdate(Object obj) {
System.out.println("更新实体Bean前:" + obj.getClass().getName());
}
@PostUpdate
public void PostUpdate(Object obj) {
System.out.println("更新实体Bean后:" + obj.getClass().getName());
}
@PreRemove
public void PreRemove(Object obj) {
System.out.println("删除实体Bean前:" + obj.getClass().getName());
}
@PostRemove
public void PostRemove(Object obj) {
System.out.println("删除实体Bean后:" + obj.getClass().getName());
}
}
此时发布代码并执行客户端测试,也会输出同样的回调信息。