Java持久性API(JPA)第7讲——实体生命周期及生命周期回调方法
源贴地址:http://blog.csdn.net/javaeeteacher/archive/2007/06/25/1665345.aspx
目标与主要内容:
u
掌握实体的生命周期;
u
掌握实体的生命周期的转换;
u
掌握实体的生命周期回调方法的使用;
1、
实体的生命周期
参考书上362页。
4种状态:
u
新建:没有持久标识,并且没有与上下文环境关联。
u
分离的:具有持久标识,并且没有与持久上下文环境关联。
u
管理的:具有持久标识,并且与持久上下文环境关联。
u
删除的:具有持久标识,并且与持久上下文环境关联,但是准备从数据库中删除。
2、
对实体的操作
对实体的各种操作可以改变实体的状态,对这些操作分别介绍如下:
2.1 持久化实体实例
通过persist方法或者被级联persist方法完成。例如可以直接持久化一个订单项,也可以通过持久化订单,该操作可以级联到订单项。
persist操作结果如下:
u
如果是新的实体,则会变成管理的;
u
如果是被管理实体,则该操作被忽略;
u
如果是删除的实体,则将重新变成被管理的;
u
如果是分离的实体,会抛出异常;
u
如果关系中声明的级联操作包括CascadeType.PERSIST,则会把级联的实体持久化。
2.2 删除实体
通过remove方法或者被级联的remove方法完成。例如可以直接删除一个订单项,也可以通过删除订单来删除订单项。
Remove操作的结果如下:
u
如果是新的实体,则该操作被忽略;
u
如果是被管理实体,则会变成删除的;
u
如果是删除的实体,则该操作被忽略;
u
如果是分离的实体,会抛出异常;
u
事务提交的时候或者作为flush操作的结果,删除的实体将从数据库中删除。
2.3 同步到数据库
通常自动完成,也可以使用flush操作完成。
flush操作的结果如下:
u
如果实体是管理的实体,将被同步到数据库;
u
如果是实体是删除的实体,将从数据库中删除掉。
2.4 分离实体
分离的实体可能来自:
u
事务提交;
u
事务回滚;
u
删除持久上下文;
u
管理实体管理器;
u
串行化一个实体或者通过值传递参数;
在第6讲中,在Servlet中创建的Ordertable对象作为参数传递到会话Bean中的时候,就是这种状态,所以需要先通过merge操作转换成管理的状态,然后再持久化。
2.5 合并分离的实体状态
merge操作可以完成合并操作。合并操作的语法如下:
u
如果是新的实体,则会创建一个新的管理的实体;
u
如果是被管理实体,则该操作被忽略;
u
如果是删除的实体,则产生异常;
u
如果是分离的实体,会创建一个新的管理的实体;
2.6 管理的实体实例
可以通过contains方法来判断实体实例是否被管理。
3、
实体生命周期回调方法
生命周期方法可以定义在
u
实体类;
u
超类;
u
实体类所关联的实体监听器类;
u
超类所关联的实体监听器类;
实体监听器类必须有一个无参数的构造方法。
可以通过元注释指定,也可以通过XML配置文件指定。
3.1 生命周期回调方法的定义
定义在实体类或者超类的回调方法:
void <METHOD>()
定义在实体监听器类的回调方法:
Void <METHOD>(Object o)
回调方法可以是任何访问控制类型,但是不能使用static和final修饰。
3.2 生命周期回调方法使用的元注释
使用的元注释及含义如下:
u
PrePersist,持久化之前产生该事件
u
PostPersist,持久化之候产生该事件
u
PreRemove,删除之前产生该事件
u
PostRemove,删除之后产生该事件
u
PreUpdate,更新之前产生该事件
u
PostUpdate,更新之后产生该事件
u
PostLoad,加载之后产生该事件
3.3 实例
在第6讲的Ordertable实体类中增加如下7个生命周期回调方法:
@PostLoad
public void postLoad(){
System.out.println("PreLoad生命周期方法被调用!");
}
@PreRemove
public void preRemove(){
System.out.println("PreRemove生命周期方法被调用!");
}
@PrePersist
public void prePersist(){
System.out.println("PrePersist生命周期方法被调用!");
}
@PreUpdate
public void preUpdate(){
System.out.println("PreUpdate生命周期方法被调用!");
}
@PostPersist
public void postPersist(){
System.out.println("PostPersist生命周期方法被调用!");
}
@PostRemove
public void postRemove(){
System.out.println("PostRemove生命周期方法被调用!");
}
@PostUpdate
public void postUpdate(){
System.out.println("PostUpdate生命周期方法被调用!");
}
@Remove
public void remove(){
System.out.println("remove方法被调用!");
}
如果要持久化一个订单,会得到如下的输出结果:
PrePersist生命周期方法被调用!
PostPersist生命周期方法被调用!
如果要调用下面的代码:
Ordertable order = em.find(Ordertable.class,orderid);
Orderdetail item = new Orderdetail(orderid,goodsid);
em.persist(item);
item.setQuantity(quantity);
if(order.getOrderdetailCollection()==null)
order.setOrderdetailCllection(new Vector<Orderdetail>());
order.getOrderdetailCollection().add(item);
item.setOrdertable(order);
会得到如下的输出结果:
PreLoad生命周期方法被调用!
PreLoad生命周期方法被调用!
PreUpdate生命周期方法被调用!
PostUpdate生命周期方法被调用!
从运行结果看调用了两次PreLoad方法,这与持久性提供者有关。
如果执行下面的代码:
Ordertable order = em.find(Ordertable.class,orderid);
em.remove(order);
会得到如下的输出结果:
PreLoad生命周期方法被调用!
PreLoad生命周期方法被调用!
PreRemove生命周期方法被调用!
PostRemove生命周期方法被调用!
因为要先查找到实体才能删除,所以调用了PreLoad方法。
删除也可以使用下面的方法:
em.createQuery("delete from Ordertable o where o.orderid = ?1").setParameter(1,orderid).executeUpdate();
3.4 一个生命周期事件的多个回调方法
同一个生命周期事件可以定义多个回调方法,可以在实体类、实体类的超类或者监听类上,如果定义了同一个生命周期事件的多个回调方法,则调用这些方法的顺序为:
如果有默认的监听器类,先调用默认的监听器类;
如果有实体监听器类,则先调用父层次的实体监听器类,后调用子层次的实体监听器类,如果在同一个实体类上定义了多个实体监听器类,按照定义的顺序执行;
如果父层次定义了生命周期回调方法,调用父层次的生命周期回调方法;
调用子层次的生命周期回调方法。
详细实例参考书上367页。
勉強心を持てば、生活は虚しくない!