原文: http://www.cnblogs.com/wanglonghai/p/4866512.html
mysql的引擎常用的有两个,一个MyISAM,另一个是InnoDB,mysql默认的为MyISAM,而InnoDB才是支持事务的。所以一般需要修改下,如何修改就不说了。
事务需要依赖数据库,好久没使用声明式事务,今天试了下。关键配置如下。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
< tx:advice id="transactionAdvice"
transaction-manager="transactionManager"> < tx:attributes > < tx:method name="add*"
propagation="REQUIRED" /> < tx:method name="append*"
propagation="REQUIRED" /> < tx:method name="insert*"
propagation="REQUIRED" /> < tx:method name="save*"
propagation="REQUIRED" /> < tx:method name="update*"
propagation="REQUIRED" /> < tx:method name="modify*"
propagation="REQUIRED" /> < tx:method name="edit*"
propagation="REQUIRED" /> < tx:method name="delete*"
propagation="REQUIRED" /> < tx:method name="remove*"
propagation="REQUIRED" /> < tx:method name="tx*"
propagation="REQUIRED" /> < tx:method name="repair"
propagation="REQUIRED" /> < tx:method name="delAndRepair"
propagation="REQUIRED" /> < tx:method name="get*"
propagation="SUPPORTS" /> < tx:method name="find*"
propagation="SUPPORTS" /> < tx:method name="load*"
propagation="SUPPORTS" /> < tx:method name="search*"
propagation="SUPPORTS" /> < tx:method name="datagrid*"
propagation="SUPPORTS" /> < tx:method name="*"
propagation="SUPPORTS" /> </ tx:attributes > </ tx:advice > < aop:config > < aop:pointcut id="transactionPointcut"
expression="execution(* com.wondersgroup.employeeBenefits.*.*.service..*Impl.*(..))" /> <!--
com.wondersgroup.benefit.*.core.service..*Impl.*(..) --> < aop:advisor pointcut-ref="transactionPointcut"
advice-ref="transactionAdvice" /> </ aop:config > |
事务配置好之后再service中手动抛了个exception,结果没有回滚,service方法如下
1
2
3
4
5
6
7
8
9
|
@Override public int saveBudgetApplyInfo(BudgetApplyInfo
budgetApplyInfo) throws Exception
{ budgetApplyInfoMapper.insert(budgetApplyInfo); if ( 1 == 1 ){ throw new Exception(); } return 1 ; } |
跟着断点一步步进去查看原因
在
TransactionAspectSupport中发现这样一个方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
|
/** *
Handle a throwable, completing the transaction. *
We may commit or roll back, depending on the configuration. *
@param txInfo information about the current transaction *
@param ex throwable encountered */ protected void completeTransactionAfterThrowing(TransactionInfo
txInfo, Throwable ex) { if (txInfo
!= null &&
txInfo.hasTransaction()) { if (logger.isTraceEnabled())
{ logger.trace( "Completing
transaction for [" +
txInfo.getJoinpointIdentification() + "]
after exception: " +
ex); } if (txInfo.transactionAttribute.rollbackOn(ex))
{ try { txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus()); } catch (TransactionSystemException
ex2) { logger.error( "Application
exception overridden by rollback exception" ,
ex); ex2.initApplicationException(ex); throw ex2; } catch (RuntimeException
ex2) { logger.error( "Application
exception overridden by rollback exception" ,
ex); throw ex2; } catch (Error
err) { logger.error( "Application
exception overridden by rollback error" ,
ex); throw err; } } else { //
We don't roll back on this exception. //
Will still roll back if TransactionStatus.isRollbackOnly() is true. try { txInfo.getTransactionManager().commit(txInfo.getTransactionStatus()); } catch (TransactionSystemException
ex2) { logger.error( "Application
exception overridden by commit exception" ,
ex); ex2.initApplicationException(ex); throw ex2; } catch (RuntimeException
ex2) { logger.error( "Application
exception overridden by commit exception" ,
ex); throw ex2; } catch (Error
err) { logger.error( "Application
exception overridden by commit error" ,
ex); throw err; } } } } |
上面的方法中有这么一段
1
|
txInfo.transactionAttribute.rollbackOn(ex),这里是判断是否需要执行回滚操作的,跟踪rollbackOn方法最后会执行到 |
DefaultTransactionAttribute中的rollbackOn方法
1
2
3
4
5
6
7
8
|
/** *
The default behavior is as with EJB: rollback on unchecked exception. *
Additionally attempt to rollback on Error. *
<p>This is consistent with TransactionTemplate's default behavior. */ public boolean rollbackOn(Throwable
ex) { return (ex instanceof RuntimeException
|| ex instanceof Error); } |
看到这里,应该都清楚了。。。自己主动抛异常Exception是不对的。这里只捕获运行时异常
1
|
RuntimeException
及Error,所以我们测试时不可以直接抛Exception,而应该换成 |
1
|
RuntimeException
。当然。也可在xml中指定rollback- for |
1
|
< tx:method name="add*"
propagation="REQUIRED" rollback-for="Exception" /> |
最后:个人还是比较喜欢基于注解的事务处理
如果还不行,请接着看下面这篇:最近做一个项目,项目的框架采用是Spring3MVC+MyBatis3.1。可是在开发过程中发现配置的事务不管用。
出现这个问题的现象是用Junit调试事务管用,而部署到Tomcat中就不管用了。先看看事务的配置:
<!--proxy-target-class="true"强制使用cglib代理 如果为false则spring会自动选择-->
<aop:aspectj-autoproxy proxy-target-class="true"/>
<!-- Transaction manager for a single JDBC DataSource -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="save*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut expression="execution(public * com.luyou.platform.service.impl.*Impl.*(..))" id="pointcut"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut"/>
</aop:config>
采用以下两种方法调试:
一、Debug视图
1、Junit下的
发现配置事务的AOP已经包进来了。再看看Tomcat中运行的Debug截图:
显然AOP没有被包进来。
二、Log4J的记录:
Junit下的记录:
Spring托管了事务。
Tomcat运行时的记录:
Spring没有托管事务。
从以上两种方法的调试说明了,事务的配置是正确的,只是在部署到Tomcat中,没有被托管。为什么会在Junit的时候就可以呢?得看看Junit的配置:
@RunWith(SpringJUnit4ClassRunner.class)
@TestExecutionListeners({DependencyInjectionTestExecutionListener.class})
@ContextConfiguration("classpath:applicationContext.xml")
这是Junit加载Spring容器的注解。ContextConfiguration会把applicationContext.xml的Bean都加载了,这就说明Tomcat在运行时没有将applicationContext.xml的Bean加载进来。问了前辈,前辈的回话是这样的:
切面配置在了root applicationContext的bean上了,而spring mvc会根据xxx-servelt.xml生成一个自己的applicationContext,他的父applicationContext为root applicatonContext,当mvc有自己的bean时便不再去向父context要bean,导致声明事务无效。
看了前辈的这个邮件,我将applicationContext.xml中配置事务的AOP复制到XXX-servlet.xml中。再调试,Tomcat中运行项目事务被Spring托管了,也就是问题解决了!!
问题解决后查看了Spring3.1的Docs发现了以下的内容:
These inherited beans can be overridden in the servlet-specific scope, and you can define new scope-specific beans local to a given Servlet instance.
Context hierarchy in Spring Web MVC Upon initialization of a DispatcherServlet, Spring MVC looks for a file named [servlet-name]-servlet.xml in the WEB-INF directory of your web application and creates the beans defined there, overriding the
definitions of any beans defined with the same name in the global scope.