在Spring中实现声明控制的事务管理3EclipseWord文档格式.docx
《在Spring中实现声明控制的事务管理3EclipseWord文档格式.docx》由会员分享,可在线阅读,更多相关《在Spring中实现声明控制的事务管理3EclipseWord文档格式.docx(27页珍藏版)》请在冰点文库上搜索。
(2)面向目标实现类
当然如果没有这样的目标接口,那么Spring会使用CGLIB来解决问题,但这不是Spring所推荐的方式(此时应该设置proxyTargetClass属性为true)。
4、TransactionProxyFactoryBean类----作为DAO组件的事务代理组件
(1)作用
要进行声明控制的事务管理,一个简化的方法是使用TransactionProxyFactoryBean组件,通过它可以指定要介入的事务管理对象及其方法。
(2)定义
(3)应用的要求
TransactionProxyFactoryBean需要一个transactionManager属性,由于我们直接使用JDBC,所以在下面的示例中继续使用DataSourceTransactionManager类。
同时还需要target属性,该属性定义需要进行事务代理的类---也就是我们的DAO组件类
最后,还需要一个transactionAttributes属性,定义具体的事务要求。
在JBuilder中实现本例中的声明式的事务管理的过程
Spring声明式事务处理中由于主要使用了IoC和AOP思想,同时提供了TransactionInterceptor拦截器和常用的代理类TransactionProxyFactoryBean,从而可以允许开发者直接以配置的方式实现对组件进行事务代理。
注意:
下面的具体实现过程是在前面的代码方式的过程的基础上进行的。
因此,省略了前面的表示层和控制层的代码的实现过程的说明------具体,可以看前面的代码方式的过程。
1、添加*.jar包文件到本项目中
2、具体的实现过程----在springapp-servlet.xml文件中再增加下面的配置
(1)添加一个TransactionProxyFactoryBean的对象声明
<
beanid="
userDAOProxy"
class="
org.springframework.transaction.interceptor.TransactionProxyFactoryBean"
>
propertyname="
proxyInterfaces"
<
list>
<
value>
springwebapp.UserLoginInterface<
/value>
/list>
<
/property>
<
transactionManager"
refbean="
/>
target"
userLoginImpleBean"
transactionAttributes"
props>
propkey="
doUpdateUserInfo*"
PROPAGATION_REQUIRED<
/prop>
*"
PROPAGATION_REQUIRED,readOnly<
/props>
/bean>
对上面的各个标签的说明:
●TransactionProxyFactoryBean是个代理类,其target属性指定要代理的对象(UserDAOProxy节点配置了一个针对userLoginImpleBean的事务代理),事务管理会自动地介入指定的方法前后。
由于工程大部分的类都要数据库操作,如果每次都要实例化这个bean(“userDAOProxy”这个bean)的话,会非常消耗资源的----Spring默认都是被管理的对象都是单例的。
●proxyInterfaces:
代理类应该实现的接口列表
●这里,我们通过transactionAttributes属性指定了事务的管理策略,doUserRegister*表示指定方法名称doUserRegister开头的都要納入事务管理,我们也可以指定方法的全名,如果在方法执行过程中发生了错误(抛出异常),則所有操作自动撤回,否則正常提交。
●doUserRegister*等方法上指定了PROPAGATION_REQUIRED,表示在目前的事务中执行操作,如果事务不存在就建立一个新的,相关的常量的含义都可以在API文件中TransactionDefinition接口中找到。
另一方面,对于其他方法(通过通配符*表示),则进行只读事务管理,以获得更好的性能----不会读取未提交的数据,同时它的数据为只读(可提高执行速度)----如可以避免对脏数据的检查。
PROPAGATION_REQUIRED,readOnly<
Spring的事务属性支持一个称为“回滚规则”的概念
默认情况下,任何RuntimeException或Error的抛出均会导致回滚。
当然,我们也可以加上多个事务的定义,中间使用逗号"
"
隔离开,例如我们可以加上只读,或者是指定某个异常产生时(并告诉事务代理,在抛出该异常时执行进行回滚),执行回滚操作。
PROPAGATION_REQUIRED,readOnly,-ExamerException
上面的这个事务策略表示某个方法将需要一个事务支持,同时当在事务过程中,如果产生了ExamerException异常,事务将会回滚。
在MyCheckedException前面加上“-”時,表示产生指定的异常时撤消操作,如果前面加上"
+"
,表示产生指定的异常时立即提交。
下面列出事务属性各个参数的含义
●PROPAGATION_REQUIRED--支持当前事务,如果当前没有事务,就新建一个事务。
这是最常见的选择。
PROPAGATION_REQUIRED等同于EJB中的TX_REQUIRED
其实现的策略为
如果我们希望服务方法一直在事务中运行,就可以使用PROPAGATION_REQUIRED。
我们使用PROPAGATION_REQUIRED的时候,如果某个TX已经在运行中,那么bean方法加入那个TX,否则Spring轻量级TX管理器将为你重新启动一个。
●PROPAGATION_SUPPORTS--支持当前事务,如果当前没有事务,就以非事务方式执行。
●PROPAGATION_MANDATORY--支持当前事务,如果当前没有事务,就抛出异常。
●PROPAGATION_REQUIRES_NEW--新建事务,如果当前存在事务,把当前事务挂起---这可以保证它将始终运行在一个事务中。
PROPAGATION_REQUIRES_NEW等同于EJB中的TX_REQUIRES_NEW
如果我们希望在组件服务被调用的时候,一般情况下启动新事务,那么就可以使用PROPAGATION_REQUIRES_NEW属性了。
●PROPAGATION_NOT_SUPPORTED--以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
●PROPAGATION_NEVER--以非事务方式执行,如果当前存在事务,则抛出异常。
●PROPAGATION_NESTED--如果当前存在事务,则在嵌套事务内执行。
如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。
前六个策略类似于EJBCMT中的常量名相同(请见下面的EJB中的事务的属性说明),因此,对EJB开发人员来说,应该立刻就感到熟悉。
第七个(PROPAGATION_NESTED)是Spring所提供的一个特殊变量。
它要求事务管理器或者使用JDBC3.0SavepointAPI提供嵌套事务行为(如Spring的DataSourceTransactionManager),或者通过JTA支持嵌套事务。
EJB中的事务的属性说明
●Nerver:
不参与事务,如果参与产生RemoteException
●NotSupported:
不能参与
●Supports:
如果调用者正在参与事务,相应的EJB调用也可以参与事务,否则不能
●Mandatory如果调用者有一个事务,相应的EJB可以参与事务,否则,TransactionRequiredException
●Required如果调用者有一个事务,相应的EJB可以参与事务,否则,容器将在调用相应的EJB之前,开始一个事务。
●当方法调用完成以后,即提交该事务。
●RequiresNew在调用相应的EJB之前,开始一个新的事务,当方法调用返回时,即提交这个事务。
嵌套事务类型(PROPAGATION_NESTED),是相对上面提到的六种情况(上面的六种应该称为平面事务类型),打个比方我现在有一个事务主要有一下几部分:
●从A用户帐户里面减去100元钱
●往B用户帐户里面添加100元钱
这样看和以前不同的事务可能没有什么区别,那我现在有点特殊的要求就是,A用户有3个帐户,B用户有2个帐户,现在我的要求就是只要再A用户的3个帐户里面任意一个减去100元,往B用户的两个帐户中任意一个里面增加100元就可以了!
一旦我们有这样的要求,那嵌套事务类型就非常适合我们的需要!
我们可以这样理解:
●将“从A用户帐户里面减去100元钱”和“往B用户帐户里面增加100元钱”我们暂时认为是一级事务操作
●将从A用户的3个帐户的任意一个帐户里面减钱看做是“从A用户帐户里面减去100元钱”这个一级事务的子事务(二级事务),同样把后面存钱的看成是另一个的二级事务。
(2)transactionManager的配置继续保留
org.springframework.jdbc.datasource.DataSourceTransactionManager"
dataSource"
3、具体的实现过程----在UserLoginController类中获得userDAOProxy,并将它转换为业务组件类的对象
(1)在我们的*.xml配置文件中删除userLoginImpleBean的属性引入,但添加对userDAOProxy的属性成员。
userLoginController"
springwebapp.UserLoginController"
commandClass"
springwebapp.UserLoginForm<
updateSuccess"
index<
updateFailure"
userLogin/updateUserInfo<
loginSuccess"
userLogin/loginSuccess<
loginFailure"
userLogin/loginFailure<
registerSuccess"
userLogin/registerSuccess<
registerFailure"
userLogin/registerFailure<
(2)因此,同样也应该在UserLoginController类中删除原来的userLoginImpleBean的get和set方法
privateUserLoginInterfaceuserLoginImpleBean=null;
publicvoidsetUserLoginImpleBean(UserLoginInterfacenewUserLoginImpleBean)
{
userLoginImpleBean=newUserLoginImpleBean;
}
publicUserLoginInterfacegetUserLoginImpleBean()
returnuserLoginImpleBean;
(3)同时再增加对userDAOProxy成员属性的get/set方法
privateUserLoginInterfaceuserDAOProxy=null;
publicUserLoginInterfacegetUserDAOProxy()
{
returnuserDAOProxy;
publicvoidsetUserDAOProxy(UserLoginInterfaceuserDAOProxy)
this.userDAOProxy=userDAOProxy;
(4)最后,在UserLoginController类的各个调度方法中直接利用userDAOProxy对象来对业务方法进行调用-----所应该注意的是,对doUserRegister和doUserLogin两个方法也应该修改。
publicModelAndViewdoUpdateUserInfo(UserLoginFormuserLoginForm)
StringuserName=userLoginForm.getUserName();
StringuserPassWord=userLoginForm.getUserPassWord();
UserInfoVOoneUserInfoVO=newUserInfoVO();
oneUserInfoVO.setUserName(userName);
oneUserInfoVO.setUserPassWord(userPassWord);
booleanokOrNot=userLoginImpleBean.doUpdateUserInfo(oneUserInfoVO);
booleanokOrNot=userDAOProxy.doUpdateUserInfo(oneUserInfoVO);
if(okOrNot)
returnnewModelAndView(this.getUpdateSuccess());
else
returnnewModelAndView(this.getUpdateFailure());
由于UserLoginImple类的userLoginImpleBean对象被userDAOProxy代理了,所以我們要作的是取得userDAOProxy,而不是userLoginImpleBean。
Spring中的事务管理实际上是基于动态AOP机制实现,为了实现动态AOP,Spring在默认情况下会使用JavaDynamicProxy,但是,DynamicProxy要求其代理的对象必须实现一个接口---本例为UserLoginInterface,该接口定义了准备进行代理的方法---本例为doUserRegister方法。
(5)控制器组件的最后代码为下面
所应该注意的是,对doUserRegister和doUserLogin两个方法也应该修改。
packagespringwebapp;
importorg.springframework.web.servlet.mvc.SimpleFormController;
importorg.springframework.web.servlet.ModelAndView;
importjavax.servlet.http.HttpServletRequest;
importjavax.servlet.http.HttpServletResponse;
publicclassUserLoginControllerextendsSimpleFormController{
publicvoidsetLoginFailure(StringloginFailure){
this.loginFailure=loginFailure;
publicvoidsetLoginSuccess(StringloginSuccess){
this.loginSuccess=loginSuccess;
publicUserLoginInterfacegetUserLoginImpleBean(){
publicStringgetLoginFailure(){
returnloginFailure;
publicStringgetLoginSuccess(){
returnloginSuccess;
publicUserLoginController(){
protectedModelAndViewonSubmit(ObjectformBean)throwsException
UserLoginFormuserLoginForm=(UserLoginForm)formBean;
ModelAndViewtargetModelAndView=null;
switch(Integer.parseInt(userLoginForm.getMenuID())){
case1:
//用户登录
targetModelAndView=doUserLogin(userLoginForm);
break;
case2:
//用户注册
targetModelAndView=doUserRegister(userLoginForm);
case3:
//修改用户信息
targetModelAndView=doUpdateUserInfo(userLoginForm);
returntargetModelAndView;
booleanokOrNot=userDAOProxy.doUpdat