在ejb组件设计中,组件行为有时需要具备事务特征,即使用事务保证组件行为的完整性,使组件要么执行成功,要么回到起点.等价没有执行!
讨论事务时要明确两个概念:
事务范围
算法的可恢复性与事务性:如果事务中的算法动作结果受事务提交或者回滚影响,则该算法具有事务可恢复性,否则就不具备事务可恢复性.
事务在本质上就是一个管理系列算法动作一致性的对象!ejb提供了两种事务类型:声明性和程序性事务
声明性事务:由ejb容器负责事务对象的 生成,管理,销毁;具体算法在事务上的注册,事务的提交和回滚等
主要介绍声明性的事务:
ejb容器为其中的
所有的ejb组件提供了一种默认的事务模式:Requires
在该模式下面,组件中的方法如果在事务环境下被调用(即客户端调用了该组件的方法),则方法中的逻辑会被添加到客户端的事务环境中,和客户端的程序使用相同的事务控制对象.如果不在事务环境中调用(在客户端没有调用该组件中的方法),ejb容器就创建新的事务对象,管理该方法中的所有逻辑.
例如:
ejb组件:
@Statefull
public class personmanager inplements IPersonAdmin{
@PersistenceContext(unitName="mydb")
private EntityManager manager;
@EJB(mappedName="MySession/remote")
private IPersonAdmin mytools;
public List<person> QueryAll(){
....
}
public void createPerson(Person p){
Person pobj=new Person();
pobj.setName("ziaoxioa");
#1 manager.persist(pobj);
#2 mytools.createPerson(p);
#3 manager.persist(null);
}
客户代码:
...
myinf.createPerson(p);
...
默认情况下,ejb读物起会为ejb组件中的所有方法配置一中requires型的事务服务.在这种事务模式下面,如果方法被在事务环境下调用,方法就使用客户端事务对象和事务环境ruguo不在事务环境中被调用,则ejb容器就会创建新的事务对象和环境来负责管理该方法的逻辑完整性!由于上面的#3错误,所以数据库中不会添加任何记录!
ejb容器为ejb组件添加的默认的事务策略能够满足多数情况下的算法要求,特殊情况下,可以通过@TransactionAttribute和<container-transaction>标记修改组件的默认事务策略!如下:
<ejb-jar
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/ejb-jar_3_0.xsd"
version="3.0">
<assembly-descriptor>
<container-transaction>
<method>
<ejb-name>MySession</ejb-name>
<method-name>createPerson</method-name>
</method>
<trans-attribute>RequiresNew</trans-attribute>
</container-transaction>
</assembly-descriptor>
</ejb-jar>
@TransactionAttribute(REQUIRES_NEW)
public void createPerson(Person p){
manager.persist(p);
}
这样MySession中的createPerson方法事务修改成了RequiesNew模式,在这种模式下,ejb容器不论客户端是否具有事务特征,为createPerson创建一个新的事务对象,此时两个createPerson在完全不同的事务对象控制下,所以#2可以提交到数据库中,#1,#3则回滚
声明性事务模式:
Required |
ejb容器为ejb组件方法提供的默认事务模式,在这种模式下,如果调用程序具备自身的事务对象,则组件方法就和客户程序使用相同的事务对象,否则ejb容器就会创建新的事务对象来管理组件方法逻辑 |
Supports |
在这种模式下,如果调用程序具备事务特征,则组件方法就会调用调用程序事务对象管理自身逻辑,如果调用程序不包含任何的事务对象,则组件方法也处于无事务管理状态 |
NotSupported |
这种模式下,组件方法不支持事务,同时也会忽略掉调用程序中的事务对象,组件方法中的逻辑不受客户程序事务的提交或回滚的影响 |
RequiresNew |
在这种模式下,忽略调用程序具备的事务特征,ejb容器会新创建一个新的事务对象管理组件方法逻辑 |
Mandatory |
ejb容器不会为该方法创建新的事务管理对象。该组件方法的调用程序必须提供事务对象,处于事务环境中,否则程序将产生异常javax.ejb.EJBTransactionRequiredException
|
Never |
这种模式下,该方法不会被ejb容器提供事务对象服务,并且调用程序也不能处于事务环境中,否则将产生EJBException异常
|
下面的程序首先清楚组件上的默认事务模式,然后在为方法设定具体事务属性:
<assembly-descriptor>
<container-transaction>
<method>
<ejb-name>MySession</ejb-name>
<method-name>*</method-name>
</method>
<trans-attribute>NotSupported</trans-attribute>
</container-transaction>
<container-transaction>
<method>
<ejb-name>MySession</ejb-name>
<method-name>createPerson</method-name>
</method>
<trans-attribute>Required</trans-attribute>
</container-transaction>
</assembly-descriptor>
声明性事务的控制:
@Stateless
/*10*/public class PersonManager implements IPersonAdmin
/*11*/{
/*12*/ @Resource
/*13*/ private EJBContext ctx;
/*14*/ @PersistenceContext(unitName="mydb")
/*15*/ private EntityManager manager;
/*16*/ @EJB(mappedName="MySession/remote")
/*17*/ private IPersonAdmin mytools;
/*18*/ public List<Person> QueryAll()
/*19*/ {
/*20*/ Query q=manager.createQuery("from Person c");
/*21*/ List results=q.getResultList();
/*22*/ List<Person> result=(List<Person>)results;
/*23*/ return result;
/*24*/ }
/*25*/ public void createPerson(Person p)
/*26*/ {
/*27*/ Person pobj=new Person();
/*28*/ pobj.setName("zhanghao");
/*29*/ pobj.setAge(new Integer(32));
/*30*/ pobj.setSex("male");
/*31*/ manager.persist(pobj);
/*32*/ boolean result=ctx.getRollbackOnly();
/*33*/ if(!result)
/*34*/ {
/*35*/ ctx.setRollbackOnly();
/*36*/ }
/*37*/ }
/*38*/}
该方法使用gejb容器提供的默认的事务模式,事务会在方法结束时自动提交。
getRollbackOnly()返回房前事务的状态,true表示已经回滚,false表示没有回滚。