其他分享
首页 > 其他分享> > (02)验证Spring的事务及其7种传播机制真实案例

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

作者:互联网

  原文:

  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