ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

(02)验证Spring的事务及其7种传播机制真实案例

2020-02-28 20:03:17  阅读:360  来源: 互联网

标签:02 事务 验证 Spring Transactional public methodB testDao void


  原文:

  https://blog.csdn.net/soonfly/article/details/70305683

  https://www.cnblogs.com/dennyzhangdd/p/9602670.html

  https://blog.csdn.net/fly910905/article/details/80000242

  说起Spring的事务,仿佛是很熟悉的老朋友了,然鹅。。。现实却很残酷。。。起初我以 Spring、Mybatis、druid、Mysql尝试,发现其中一个问题,无论数据源的defaultAutoCommit设置为true或者false,事务总会自动提交。确定配置无误后,发现网上有一种说法是把数据库的autocommit设置为OFF,即关闭数据库的自动提交,且不说这样是否可以,这种方法本身就有问题。因为在代码中获取到一个Connection,执行commit即可提交,不执行commit就不会提交,完全可以由代码控制,去设置数据库本身,这很不合理。经过一番周折还是没有搞定这个问题。

  无奈之下我把Mybatis换成JdbcTemplate,终于正常了。下面基于Spring+JdbcTemplate+druid+Mysql说说事务。

  1、事务、事务传播机制的简单说明

  事务是一个单体行为,只有提交了事务,数据才会保存到数据库,否则不会保存到数据库中。事务传播行要求至少有两个东西,才可以发生传播。指的是当一个事务方法被另一个事务方法调用时,这个被调用方法的事务方法应该如何进行。例如:methodA事务方法调用methodB事务方法时,methodB是继续在调用者methodA的事务中运行呢,还是为自己开启一个新事务运行,这就是由methodB的事务传播行为决定的。

  2、defaultAutoCommit与Transactional的关系

  配置数据源时参数defaultAutoCommit设置为ture、false代表自动、不自动提交。Transactional注解也控制事务,他们有什么关系?下面用例子说明。

  (1)defaultAutoCommit为false,不使用Transactional注解。结论:不会提交

<property name="defaultAutoCommit" value="false" />
public void save() {
  testDao.save();
}

   (2)defaultAutoCommit为false,使用Transactional注解。结论:会提交

<property name="defaultAutoCommit" value="false" />
@Transactional
public void save() {   testDao.save(); }

  (3)defaultAutoCommit为true,不使用Transactional注解。结论:会提交

<property name="defaultAutoCommit" value="true" />
public void save() {
  testDao.save();
}

  (4)defaultAutoCommit为true,使用Transactional注解。结论:会提交

<property name="defaultAutoCommit" value="true" />
@Transactional
public void save() {
  testDao.save();
}

  总结:只要defaultAutoCommit或者Transactional有一项设置为可提交即可。

  3、Transactional与异常自动回滚的关系

  在项目中希望当方法产生异常时自动回滚事务,下面我们在defaultAutoCommit设置为false的情况下进行验证

<property name="defaultAutoCommit" value="false" />

  (1)使用Transactional的默认配置,抛出检查型异常。事务不会回滚

@Transactional
public void save () throws Exception {
  testDao.save();
  throw new ClassNotFoundException();
}

  (2)使用Transactional的默认配置,抛出运行时异常。事务会回滚

@Transactional
public void save (){
  testDao.save();
  throw new RuntimeException();
}

  (3)使用Transactional注解,指定rollbackFor为抛出的异常或其父类时,检查型异常会回滚

@Transactional(rollbackFor=Exception.class)
public void save () throws Exception {
  testDao.save();   throw new ClassNotFoundException(); }

  (4)使用Transactional注解,指定rollbackFor不是抛出的异常或其父类时,运行时异常会回滚(运行时异常与rollbackFor无关,肯定回滚)

@Transactional(rollbackFor=FileNotFoundException.class)
public void save () throws Exception {
  testDao.save();
  throw new RuntimeException();
}

  (5)使用Transactional注解,捕获异常,事务不会回滚

@Transactional
public void save () throws Exception {
  try {
    testDao.save();
    throw new RuntimeException();
  } catch (Exception e) {
  // TODO: handle exception
  }
}

@Transactional
public void save () throws Exception {
  try {
    testDao.save();
    throw new ClassNotFoundException();
  } catch (Exception e) {
    // TODO: handle exception
  }
}

  (6)捕获的异常需要手动回滚,手动回滚时检查型异常可以不指定rollbackFor

@Transactional
public void save () {
  try {
    testDao.save();
    throw new ClassNotFoundException();
  } catch (Exception e) {
    TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); 
  }
}

@Transactional(rollbackFor=FileNotFoundException.class)
public void save () {
  try {
    testDao.save();
    throw new ClassNotFoundException();
  } catch (Exception e) {
  TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); 
  }
}

@Transactional
public void save () {
  try {
    testDao.save();
    throw new RuntimeException();
  } catch (Exception e) {
    TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); 
  }
}

  (7) Transactional要加在主动(直接)调用的方法上面,以下事务不会提交,没有开启事务(spring容器管理的类直接调用test)

public void test(){
    save();
}

@Transactional
public void save () {
    try {
        testDao.save();
        throw new RuntimeException();
    } catch (Exception e) {
    }
}    

  4、spring中的事务传播行为

  spring中共有7种事务传播行为,分别介绍如下:

  (1)PROPAGATION_REQUIRED:如果存在一个事务,则支持当前事务。如果没有事务则开启一个新的事务。

  方法A加注解,方法B也加注解,当方法A运行时会开启事务A,调用方法B时,方法B也加入到事务A中

 @Transactional(propagation = Propagation.REQUIRED)
 public void methodA() {
   methodB();
   testDao.methodA();
 }

 @Transactional(propagation = Propagation.REQUIRED)
 public void methodB() {
   testDao.methodB();
 }

   如上图,总共开启了一个事务。

  (2)PROPAGATION_SUPPORTS:如果存在一个事务,支持当前事务,如果没有事务,则不会开启事务。

@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
  methodB();
  testDao.methodA();
}

@Transactional(propagation = Propagation.SUPPORTS)
public void methodB() {
  testDao.methodB();
}

  如果直接调用methodA,methodA会开启一个事务,methodA调用methodB,则methodB支持当前methodA开启的事务,如下图:

  如果直接调用methodB,不会开启事务,如下图:

  如果直接调用methodA,由于methodA是SUPPORTS,不会开始事务,methodB不是直接调用,也不会开启事务

@Transactional(propagation = Propagation.SUPPORTS)
public void methodA() {
  methodB();
  testDao.methodA();
} @Transactional(propagation = Propagation.REQUIRED)   public void methodB() {   testDao.methodB(); }

  (3)PROPAGATION_MANDATORY:必须在一个事务中运行,否则报异常

@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
methodB();
testDao.methodA();
}

@Transactional(propagation = Propagation.MANDATORY)   public void methodB() {   testDao.methodB(); }

  直接调用methodA,开启一个事务,methodB也在该事务中运行

  直接调用methodB,报异常 No existing transaction found for transaction marked with propagation 'mandatory'

 @Transactional(propagation = Propagation.MANDATORY)
 public void methodB() {
   testDao.methodB();
 }

  (4)PROPAGATION_REQUIRES_NEW:开启一个新事务。如果一个事务已经存在,则先将存在的事务挂起,执行完这个新事务,再执行挂起的事务,两个事务的成功或失败没有联系。

@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
  methodB();
  testDao.methodA();
}
@Transactional(propagation = Propagation.REQUIRES_NEW) public void methodB() {   testDao.methodB(); }

  从上图中看到,并没有挂起旧事务,先执行新事务,因为只有使用JtaTransactionManager作为事务管理器时才生效。后面再研究。。。

  (5)PROPAGATION_NOT_SUPPORTED:在非事务中运行。如果有事务正在运行,他将在运行期被挂起,直到这个事务提交或者回滚才恢复执行。

 @Transactional(propagation = Propagation.REQUIRED)
 public void methodA() {
   methodB();
    testDao.methodA();
 }

 @Transactional(propagation = Propagation.NOT_SUPPORTED)
 public void methodB() {
   testDao.methodB();
 }

  直接调用methodA,运行到methodB,事务应该挂起,即methodB对应的数据不会保存到数据库。

 

 

  但上图与预期的不一致,因为也需要JtaTransactionManager作为事务管理器 。

  直接调用methodB不会开启事务,可以自己尝试一下。

  (6)PROPAGATION_NEVER:总是非事务地执行,如果存在一个活动事务,则抛出异常。

@Transactional(propagation = Propagation.NEVER)
public void methodB() {
  testDao.methodB();
}

  直接调用methodB,不会开启事务

 @Transactional(propagation = Propagation.REQUIRED)
 public void methodA() {
   methodB();
   testDao.methodA();
 }

 @Transactional(propagation = Propagation.NEVER)
 public void methodB() {
   testDao.methodB();
 }

  直接调用methodA,报异常,发现下面日志没有报异常,,,是版本问题还是我的理解有误???先留个疑问吧

 

  (7) PROPAGATION_NESTED:如果一个活动的事务存在,则运行在一个嵌套的事务中。 如果没有活动事务, 则按PROPAGATION_REQUIRED属性执行。

 

 

 

 

 

 

 

 

 

 

 

 

PROPAGATION_SUPPORTS

标签:02,事务,验证,Spring,Transactional,public,methodB,testDao,void
来源: https://www.cnblogs.com/javasl/p/12334583.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有