原文:

  mysql的引擎常用的有两个,一个MyISAM,另一个是InnoDB,mysql默认的为MyISAM,而InnoDB才是支持事务的。所以一般需要修改下,如何修改就不说了。

   事务需要依赖数据库,好久没使用声明式事务,今天试了下。关键配置如下。


<         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方法如下

@Override        
                  public          int          saveBudgetApplyInfo(BudgetApplyInfo budgetApplyInfo)          throws          Exception {        
                  budgetApplyInfoMapper.insert(budgetApplyInfo);        
                  if         (         1         ==         1         ){        
                  
                  throw          new          Exception();        
                  }        
                  return          1         ;        
                  }


  跟着断点一步步进去查看原因


TransactionAspectSupport中发现这样一个方法


/**        
                  * 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;        
                  }        
                  }        
                  }        
                  }


  上面的方法中有这么一段


txInfo.transactionAttribute.rollbackOn(ex),这里是判断是否需要执行回滚操作的,跟踪rollbackOn方法最后会执行到


DefaultTransactionAttribute中的rollbackOn方法


/**        
                  * 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是不对的。这里只捕获运行时异常


RuntimeException 及Error,所以我们测试时不可以直接抛Exception,而应该换成


RuntimeException 。当然。也可在xml中指定rollback-         for


<         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下的

mysql 事务回滚锁表时间 mysql的事务回滚_mysql 事务回滚锁表时间

发现配置事务的AOP已经包进来了。再看看Tomcat中运行的Debug截图:

mysql 事务回滚锁表时间 mysql的事务回滚_mybatis_02

显然AOP没有被包进来。


二、Log4J的记录:

Junit下的记录:

mysql 事务回滚锁表时间 mysql的事务回滚_mybatis_03

Spring托管了事务。

Tomcat运行时的记录:

mysql 事务回滚锁表时间 mysql的事务回滚_事物管理_04

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.