转载

Spring事务机制的疑惑

如下配置:

<!-- 配置事务详情
id表示事务通知名称
transaction-manager配置事务管理器
tx:attributes事务属性

tx:method事务应用的方法,相当于prop.key
no-rollback-for配置遇到就提交的异常
rollback-for 配置遇到就混回滚的异常


-->

<!-- more -->

 <tx:adviceid="TransactionId"transaction-manager="txManager">

  <tx:attributes>
   <tx:methodname="*"propagation="REQUIRED"rollback-for="Throwable"/>

  </tx:attributes>

 </tx:advice>

 <aop:config>
 <!-- 此处如果是*.*那么会对dao层的方法也进行代理,从而无法实例化 ,所以注明只对service类进行切面点,如果该业务层有dao方法要注意排除dao方法-->
  <aop:advisoradvice-ref="TransactionId"pointcut="(execution(* Demo21AopTransaction.AccountServiceImpl.*(..) ))"/>
  <!-- &&(!execution(* Demo21AopTransaction.AccountServiceImpl.q(..))) -->
 </aop:config>

 <!-- 配置事务管理器,事务管理器是从连接池中取得连接的 -->
 <beanid="txManager"class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  <propertyname="dataSource"ref="dataSourceId"></property>

 </bean>

此时

 @Test
 public void Demo()throws Exception{
  ApplicationContext app = new ClassPathXmlApplicationContext("Demo21AopTransaction/applicationContext.xml");
  AccountService service = (AccountService) app.getBean("accountServiceImpl");
// service.transfer("aaa", "bbb", 100);
  service.q();
  service.a();
 }

查看日志可以看到在执行service的a和q方法时,每一次方法的执行都进行了

申请数据库连接->得到数据库连接->开启事务->提交事务->归还连接

在一些不需要重用的业务逻辑中,完全可以将a和q方法变为一个方法执行,可以提高效率。

问题2

如下代码写法不正规,纯粹为了测试的方便,别效仿哦.



@Override
public void transfer(final String inUser,final String outUser,final int money) throws Exception{  //该方法进行转账操作

 accountDao.out(outUser, money);
 //int i=1/0;//模拟突发断电
 accountDao.in(inUser, money);
}


@Override
public void q() {
 // TODO Auto-generated method stub
 AccountService service = (AccountService) AopContext.currentProxy(); //获取代理对象
 System.out.println("q开始");

 try
 {
  service.transfer("aaa", "bbb", 200);
 } catch (Exception e)
 {
  // TODO Auto-generated catch block
  e.printStackTrace();
 }
 System.out.println("断电");
 int i =1 /0; //模拟发生了错误 抛出异常
 System.out.println("q结束");
}

查看日志如下:

[QC] DEBUG [main] org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(367) | Creating new transaction with name [Demo21AopTransaction.AccountServiceImpl.q]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT,-Throwable
   [QC] DEBUG [main] org.springframework.jdbc.datasource.DataSourceTransactionManager.doBegin(206) | Acquired Connection [ProxyConnection[PooledConnection[com.mysql.jdbc.JDBC4Connection@34652b13]]] for JDBC transaction
   [QC] DEBUG [main] org.springframework.jdbc.datasource.DataSourceTransactionManager.doBegin(223) | Switching JDBC Connection [ProxyConnection[PooledConnection[com.mysql.jdbc.JDBC4Connection@34652b13]]] to manual commit
   q开始
[QC] DEBUG [main] org.springframework.transaction.support.AbstractPlatformTransactionManager.handleExistingTransaction(476) | Participating in existing transaction  //1

只在q方法的开始之前开启了一次事务。在通过代理对象调用transfer方法的时候通过事务传播机制(required) transfer方法也共享了该事务。查看数据库,后续的’断电’.也确实引起了rollback。

但我测试发现 直接在q方法中调用transfer 而不通过代理对象调用transfer,q方法中的异常依旧会使transfer的结果回滚

按道理说直接调用的transfer并不会被代理。参考我的另一篇文章 java动态代理



 @Override
 public void transfer(final String inUser,final String outUser,final int money) throws Exception{  //该方法进行转账操作

  accountDao.out(outUser, money);
  //int i=1/0;//模拟突发断电
  accountDao.in(inUser, money);
 }


 @Override
 public void q() {
  // TODO Auto-generated method stub

  System.out.println("q开始");

  try
  {
transfer("aaa", "bbb", 200);
  } catch (Exception e)
  {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
  System.out.println("断电");
  int i =1 /0; //模拟发生了错误 抛出异常
  System.out.println("q结束");
 }

对比两种情况下的日志输出。

后一种情况日志并无

[QC] DEBUG [main] org.springframework.transaction.support.AbstractPlatformTransactionManager.handleExistingTransaction(476) | Participating in existing transaction

可见是某种机制使得transfer方法并未被代理但q的事务仍旧有效

原文  http://www.woainia.site/2018/03/20/Spring事务机制的疑惑/
正文到此结束
Loading...