01 前言:
- 建议先阅读原作者JerryTse关于spring事务传播行为的详解 :Spring事务传播行为详解
- 该部分代码源于https://github.com/TmTse/transaction-test,对注释位置进行了部分调整,方便阅读。
02 代码背景:
模拟向user1、user2两张数据库表中插入数据,演示使用不同的事务传播行为,在不同的位置模拟产生异常,观察不同位置的异常对事务传播行为的影响。
03 示例代码中方法名解释:
// 外层方法名示例:
notransaction_required_required()
第一个位置表示外层方法是否有事务、后面依次是内层被调用的方法的事务传播行为。
transaction_exception_notransaction_notransaction()
表示外层方法有事务(第一个位置),且有异常(第二个位置),内层调用的两个方法都没有事务(第三、第四个位置)
notransaction_exception_required_required()
表示外层方法没有事务(第一个位置),且有异常(第二个位置),内层调用的两个方法的事务传播行为已设置为required(第三、四个位置)
// 内层方法名示例:
addRequired()
表示该方法的事务传播行为已设置为required
addRequiredException(user2);
表示该方法的事务传播行为已设置为required,且方法执行时会产生异常
04 示例代码:
package org.transaction.test.local_transaction.mybatis.service.impl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.transaction.test.local_transaction.mybatis.bean.User1;
import org.transaction.test.local_transaction.mybatis.bean.User2;
import org.transaction.test.local_transaction.mybatis.service.TransactionPropagationExample;
import org.transaction.test.local_transaction.mybatis.service.User1Service;
import org.transaction.test.local_transaction.mybatis.service.User2Service;
@Service
public class TransactionPropagationExampleImpl implements TransactionPropagationExample {
@Autowired
private User1Service user1Service;
@Autowired
private User2Service user2Service;
@Override
public void truncated() {
user1Service.truncate();
user2Service.truncate();
}
/**
* 结果:张三(插入),李四(插入)</br>
*/
@Override
public void notransaction_exception_notransaction_notransaction(){
User1 user1=new User1();
user1.setName("张三");
user1Service.add(user1);
User2 user2=new User2();
user2.setName("李四");
user2Service.add(user2);
throw new RuntimeException();
}
/**
* 结果:张三(插入),李四(插入)</br>
*/
@Override
public void notransaction_notransaction_notransaction_exception(){
User1 user1=new User1();
user1.setName("张三");
user1Service.add(user1);
User2 user2=new User2();
user2.setName("李四");
user2Service.addException(user2);
}
/**
* 结果:张三(未插入),李四(未插入)</br>
* 外围方法开启事务,外围方法内的方法就应该在同一个事务中。外围方法抛出异常,整个事务所有方法都会被回滚。
*/
@Override
@Transactional
public void transaction_exception_notransaction_notransaction(){
User1 user1=new User1();
user1.setName("张三");
user1Service.add(user1);
User2 user2=new User2();
user2.setName("李四");
user2Service.add(user2);
throw new RuntimeException();
}
/**
* 结果:张三(未插入),李四(未插入)</br>
* 外围方法开启事务,外围方法内的方法就应该在同一个事务中。内部方法抛出异常,被外围方法捕获,整个事务中所有方法都会被回滚。
*/
@Override
@Transactional
public void transaction_notransaction_notransaction_exception(){
User1 user1=new User1();
user1.setName("张三");
user1Service.add(user1);
User2 user2=new User2();
user2.setName("李四");
user2Service.addException(user2);
}
/**
* 结果:张三(插入),李四(插入)</br>
* 观察方法执行,执行完插入“张三”方法后数据库即插入数据,执行完插入“李四”方法后数据库即插入数据,结合后面回滚方法,说明两个方法分别在两个事务中执行。
*/
@Override
public void notransaction_required_required(){
User1 user1=new User1();
user1.setName("张三");
user1Service.addRequired(user1);
User2 user2=new User2();
user2.setName("李四");
user2Service.addRequired(user2);
}
/**
* 结果:getRequired可见,get可见</br>
* 外围方法未开启事务,addRequired在自己的事务中运行,执行外之后即对外可见,故getRequired和get都可见事务执行结果。
*/
@Override
public void notransaction_addRequired_getRequired_get(){
User1 user1=new User1();
user1.setName("张三");
user1Service.addRequired(user1);
User1 temp1=user1Service.getRequired(1);
if(temp1!=null){
System.out.println("getRequired可见");
}
User1 temp2=user1Service.get(1);
if(temp2!=null){
System.out.println("get可见");
}
}
/**
* 结果:张三(插入),李四(插入)。</br>
* 外围方法没有事务,插入“张三”、“李四”方法在自己的事务中独立运行,外围方法异常不影响内部插入“张三”、“李四”方法独立的事务。
*/
@Override
public void notransaction_exception_required_required(){
User1 user1=new User1();
user1.setName("张三");
user1Service.addRequired(user1);
User2 user2=new User2();
user2.setName("李四");
user2Service.addRequired(user2);
throw new RuntimeException();
}
/**
* 结果:张三(插入),李四(未插入)</br>
* 外围方法没有事务,插入“张三”、“李四”方法都在自己的事务中独立运行,所以插入“李四”方法抛出异常只会回滚插入“李四”方法,插入“张三”
* 方法不受影响。
*
*/
@Override
public void notransaction_required_required_exception(){
User1 user1=new User1();
user1.setName("张三");
user1Service.addRequired(user1);
User2 user2=new User2();
user2.setName("李四");
user2Service.addRequiredException(user2);
}
/**
* 结果:张三(未插入),李四(未插入)</br>
* 外围方法开启事务,插入“张三”、“李四”方法都在外围方法的事务中运行,加入外围方法事务,所以三个方法同一个事务。外围方法或内部方法抛出异常,
* 整个事务全部回滚。
*/
@Override
@Transactional(propagation = Propagation.REQUIRED)
public void transaction_exception_required_required(){
User1 user1=new User1();
user1.setName("张三");
user1Service.addRequired(user1);
User2 user2=new User2();
user2.setName("李四");
user2Service.addRequired(user2);
throw new RuntimeException();
}
/**
* 结果:张三(未插入),李四(未插入)</br>
* 外围方法开启事务,插入“张三”、“李四”方法都在外围方法的事务中运行,加入外围方法事务,所以三个方法同一个事务。外围方法或内部方法抛出异常,
* 整个事务全部回滚。
*/
@Override
@Transactional(propagation = Propagation.REQUIRED)
public void transaction_required_required_exception(){
User1 user1=new User1();
user1.setName("张三");
user1Service.addRequired(user1);
User2 user2=new User2();
user2.setName("李四");
user2Service.addRequiredException(user2);
}
/**
* 结果:getRequired可见,get可见</br>
* 外围方法开启事务,addRequired和外围方法同事务,getRequired和get都和外围方法同一个事务,故均可见addRequired执行之后的结果。
*/
@Override
@Transactional(propagation = Propagation.REQUIRED)
public void transaction_addRequired_getRequired_get(){
User1 user1=new User1();
user1.setName("张三");
user1Service.addRequired(user1);
User1 temp1=user1Service.getRequired(1);
if(temp1!=null){
System.out.println("getRequired可见");
}
User1 temp2=user1Service.get(1);
if(temp2!=null){
System.out.println("get可见");
}
}
/**
* 结果:getNested可见,get可见</br>
* 外围方法开启事务,addRequired和外围方法同事务,getNested属于外围事务子事务,get属于外围事务,故均可见addRequired执行之后的结果。
*/
@Override
@Transactional(propagation = Propagation.REQUIRED)
public void transaction_addRequired_getNested_get(){
User1 user1=new User1();
user1.setName("张三");
user1Service.addRequired(user1);
User1 temp1=user1Service.getNested(1);
if(temp1!=null){
System.out.println("getNested可见");
}
User1 temp2=user1Service.get(1);
if(temp2!=null){
System.out.println("get可见");
}
}
/**
* 结果:张三(插入),李四(插入)</br>
* 外围方法未开启事务,插入“张三”、“李四”方法也均未开启事务,因为不存在事务所以无论外围方法或者内部方法抛出异常都不会回滚。
*/
@Override
public void notransaction_supports_supports_exception(){
User1 user1=new User1();
user1.setName("张三");
user1Service.addSupports(user1);
User2 user2=new User2();
user2.setName("李四");
user2Service.addSupportsException(user2);
}
/**
* 结果:张三(插入),李四(插入)</br>
* 外围方法未开启事务,插入“张三”、“李四”方法也均未开启事务,因为不存在事务所以无论外围方法或者内部方法抛出异常都不会回滚。
*/
@Override
public void notransaction_exception_supports_supports(){
User1 user1=new User1();
user1.setName("张三");
user1Service.addSupports(user1);
User2 user2=new User2();
user2.setName("李四");
user2Service.addSupports(user2);
throw new RuntimeException();
}
/**
* 结果:张三(未插入),李四(未插入)</br>
* 外围方法开启事务,插入“张三”、“李四”方法都在外围方法的事务中运行,加入外围方法事务,所以三个方法同一个事务。外围方法或内部方法抛出异常,
* 整个事务全部回滚。
*/
@Override
@Transactional(propagation = Propagation.REQUIRED)
public void transaction_supports_supports_exception(){
User1 user1=new User1();
user1.setName("张三");
user1Service.addSupports(user1);
User2 user2=new User2();
user2.setName("李四");
user2Service.addSupportsException(user2);
}
/**
* 结果:张三(未插入),李四(未插入)</br>
* 外围方法开启事务,插入“张三”、“李四”方法都在外围方法的事务中运行,加入外围方法事务,所以三个方法同一个事务。外围方法或内部方法抛出异常,
* 整个事务全部回滚。
*/
@Override
@Transactional(propagation = Propagation.REQUIRED)
public void transaction_exception_supports_supports(){
User1 user1=new User1();
user1.setName("张三");
user1Service.addSupports(user1);
User2 user2=new User2();
user2.setName("李四");
user2Service.addSupports(user2);
throw new RuntimeException();
}
/**
* 结果:张三(插入),李四(未插入)</br>
* 外围方法未开启事务,插入“张三”、“李四”方法都在自己的事务中独立运行。插入“李四”方法抛出异常只会导致插入“李四”方法中的事务被回滚,
* 不会影响插入“张三”方法的事务。
*/
@Override
public void notransaction_requiresNew_requiresNew_exception(){
User1 user1=new User1();
user1.setName("张三");
user1Service.addRequiresNew(user1);
User2 user2=new User2();
user2.setName("李四");
user2Service.addRequiresNewException(user2);
}
/**
* 结果:张三(插入),李四(插入)</br>
* 外围方法未开启事务,插入“张三”、“李四”方法都在自己的事务中独立运行。外围方法抛出异常,插入“张三”、“李四”事务均不回滚。
*/
@Override
public void notransaction_exception_requiresNew_requiresNew(){
User1 user1=new User1();
user1.setName("张三");
user1Service.addRequiresNew(user1);
User2 user2=new User2();
user2.setName("李四");
user2Service.addRequiresNew(user2);
throw new RuntimeException();
}
/**
* 结果:张三(未插入),李四(插入),王五(未插入)</br>
* 外围方法开启事务,插入“张三”方法和外围方法一个事务,插入“李四”方法、插入“王五”方法分别在独立的新建事务中。插入“王五”方法抛出异常,首先插入
* “王五”方法的事务被回滚,异常继续抛出被外围方法感知,外围方法事务亦被回滚,故插入“张三”方法也被回滚。
*/
@Override
@Transactional(propagation = Propagation.REQUIRED)
public void transaction_required_requiresNew_requiresNew_exception(){
User1 user1=new User1();
user1.setName("张三");
user1Service.addRequired(user1);
User2 user2=new User2();
user2.setName("李四");
user2Service.addRequiresNew(user2);
User2 user3=new User2();
user3.setName("王五");
user2Service.addRequiresNewException(user3);
}
/**
* 结果:张三(插入),李四(插入),王五(未插入)</br>
* 外围方法开启事务,插入“张三”方法和外围方法一个事务,插入“李四”方法、插入“王五”方法分别在独立的新建事务中。插入“王五”方法抛出异常,首先插入
* “王五”方法的事务被回滚,异常被catch不会被外围方法感知,外围方法事务不回滚,故插入“张三”方法插入成功。
*/
@Override
@Transactional(propagation = Propagation.REQUIRED)
public void transaction_required_requiresNew_requiresNew_exception_try(){
User1 user1=new User1();
user1.setName("张三");
user1Service.addRequired(user1);
User2 user2=new User2();
user2.setName("李四");
user2Service.addRequiresNew(user2);
User2 user3=new User2();
user3.setName("王五");
try {
user2Service.addRequiresNewException(user3);
} catch (Exception e) {
System.out.println("回滚");
}
}
/**
* 结果:张三(未插入),李四(插入),王五(插入)</br>
* 外围方法开启事务,插入“张三”方法和外围方法一个事务,插入“李四”方法、插入“王五”方法分别在独立的新建事务中,
* 外围方法抛出异常只回滚和外围方法同一事务的方法,故插入“张三”的方法回滚。
*/
@Override
@Transactional(propagation = Propagation.REQUIRED)
public void transaction_exception_required_requiresNew_requiresNew(){
User1 user1=new User1();
user1.setName("张三");
user1Service.addRequired(user1);
User2 user2=new User2();
user2.setName("李四");
user2Service.addRequiresNew(user2);
User2 user3=new User2();
user3.setName("王五");
user2Service.addRequiresNew(user3);
throw new RuntimeException();
}
/**
* 结果:get可见</br>
* 外围方法开启事务,addRequired和外围方法同事务,getRequiresNew新开事务,并将外围事务挂起,由于事务隔离性,getRequiresNew看不到addRequired的执行结果。
* ,get属于外围事务,故可见addRequired执行之后的结果。
*/
@Transactional
@Override
public void transaction_addRequired_getRequiresNew_get(){
User1 user1=new User1();
user1.setName("张三");
user1Service.addRequired(user1);
User1 temp1=user1Service.getRequiresNew(1);
if(temp1!=null){
System.out.println("getRequiresNew可见");
}
User1 temp2=user1Service.get(1);
if(temp2!=null){
System.out.println("get可见");
}
}
/**
* 结果:张三(插入),李四(插入)</br>
* 外围方法未开启事务,插入“张三”方法在自己的事务中运行,插入“李四”方法不在任何事务中运行。外围方法抛出异常,但是外围方法没有事务,
* 所以其他内部事务方法不会被回滚,非事务方法更不会被回滚。
*
*/
@Override
public void notransaction_exception_required_notSuppored(){
User1 user1=new User1();
user1.setName("张三");
user1Service.addRequired(user1);
User2 user2=new User2();
user2.setName("李四");
user2Service.addNotSupported(user2);
throw new RuntimeException();
}
/**
* 结果:张三(插入),李四(插入)</br>
* 外围方法未开启事务,插入“张三”方法在自己的事务中运行,插入“李四”方法不在任何事务中运行。外围方法抛出异常,但是外围方法没有事务,
* 所以其他内部事务方法不会被回滚,非事务方法更不会被回滚。
*
*/
@Override
public void notransaction_required_notSuppored_exception(){
User1 user1=new User1();
user1.setName("张三");
user1Service.addRequired(user1);
User2 user2=new User2();
user2.setName("李四");
user2Service.addNotSupportedException(user2);
}
/**
* 结果:张三(未插入),李四(插入)</br>
* 外围方法开启事务,因为插入“张三”方法传播为required,所以和外围方法同一个事务。插入“李四”方法不在任何事务中运行。
* 外围方法抛出异常,外围方法所在的事务将会回滚,因为插入“张三”方法和外围方法同一个事务,所以将会回滚。
*
*/
@Transactional
@Override
public void transaction_exception_required_notSuppored(){
User1 user1=new User1();
user1.setName("张三");
user1Service.addRequired(user1);
User2 user2=new User2();
user2.setName("李四");
user2Service.addNotSupported(user2);
throw new RuntimeException();
}
/**
* 结果:张三(未插入),李四(插入)</br>
* 外围方法开启事务,因为插入“张三”方法传播为required,所以和外围方法同一个事务。插入“李四”方法不在任何事务中运行。
* 插入“李四”方法抛出异常,因为此方法不开启事务,所以此方法不会被回滚,外围方法接收到了异常,所以外围事务需要回滚,因插入“张三”
* 方法和外围方法同一事务,故被回滚。
*
*/
@Transactional
@Override
public void transaction_required_notSuppored_exception(){
User1 user1=new User1();
user1.setName("张三");
user1Service.addRequired(user1);
User2 user2=new User2();
user2.setName("李四");
user2Service.addNotSupportedException(user2);
}
/**
* 结果:get可见</br>
* 外围方法开启事务,addRequired和外围方法同事务,getNotSuppored不支持事务,并将外围事务挂起,getNotSuppored不在addRequired事务范围中,由于事务隔离性,getNotSuppored看不到addRequired的执行结果。
* ,get属于外围事务,故可见addRequired执行之后的结果。
*/
@Transactional
@Override
public void transaction_addRequired_getNotSuppored_get(){
User1 user1=new User1();
user1.setName("张三");
user1Service.addRequired(user1);
User1 temp1=user1Service.getNotSupported(1);
if(temp1!=null){
System.out.println("getRequired可见");
}
User1 temp2=user1Service.get(1);
if(temp2!=null){
System.out.println("get可见");
}
}
/**
* 结果:张三(未插入)</br>
* 外围方法未开启事务。内部插入“张三”方法执行的时候因外围没有事务而直接抛出异常,具体插入方法都没有机会执行。
*
*/
@Override
public void notransaction_mandatory(){
User1 user1=new User1();
user1.setName("张三");
user1Service.addMandatory(user1);
}
/**
* 结果:张三(未插入),李四(未插入)</br>
* 外围方法开启事务,插入“张三”方法和插入“李四”方法都加入外围方法事务,外围方法抛出异常,事务回滚。
*
*/
@Transactional
@Override
public void transaction_exception_mandatory_mandatory(){
User1 user1=new User1();
user1.setName("张三");
user1Service.addMandatory(user1);
User2 user2=new User2();
user2.setName("李四");
user2Service.addMandatory(user2);
throw new RuntimeException();
}
/**
* 结果:张三(未插入),李四(未插入)</br>
* 外围方法开启事务,插入“张三”方法和插入“李四”方法都加入外围方法事务,内部方法抛出异常,整个事务回滚。
*
*/
@Transactional
@Override
public void transaction_mandatory_mandatory_exception(){
User1 user1=new User1();
user1.setName("张三");
user1Service.addMandatory(user1);
User2 user2=new User2();
user2.setName("李四");
user2Service.addMandatoryException(user2);
}
/**
* 结果:张三(插入),李四(插入)</br>
* 外围方法未开启事务,插入“张三”方法和插入“李四”方法也均无事务,任何异常都不会回滚。。
*
*/
@Override
public void notransaction_exception_never_never(){
User1 user1=new User1();
user1.setName("张三");
user1Service.addNever(user1);
User2 user2=new User2();
user2.setName("李四");
user2Service.addNever(user2);
throw new RuntimeException();
}
/**
* 结果:张三(插入),李四(插入)</br>
* 外围方法未开启事务,插入“张三”方法和插入“李四”方法也均无事务,任何异常都不会回滚。。
*
*/
@Override
public void notransaction_never_never_exception(){
User1 user1=new User1();
user1.setName("张三");
user1Service.addNever(user1);
User2 user2=new User2();
user2.setName("李四");
user2Service.addNeverException(user2);
}
/**
* 结果:张三(未插入</br>
* 外围方法开启事务。内部插入“张三”方法执行的时候因外围有事务而直接抛出异常,具体插入方法都没有机会执行。
*
*/
@Transactional
@Override
public void transaction_never(){
User1 user1=new User1();
user1.setName("张三");
user1Service.addNever(user1);
}
/**
* 结果:张三(未插入),李四(未插入)</br>
* 外围方法开启事务,插入“张三”方法和插入“李四”方法为外围方法的子事务,外围方法事务回滚,相应的子事务也会回滚。
*
*/
@Transactional
@Override
public void transaction_exception_nested_nested(){
User1 user1=new User1();
user1.setName("张三");
user1Service.addNested(user1);
User2 user2=new User2();
user2.setName("李四");
user2Service.addNested(user2);
throw new RuntimeException();
}
/**
* 结果:张三(未插入),李四(未插入)</br>
* 外围方法开启事务,插入“张三”方法和插入“李四”方法为外围方法的子事务,插入“李四”方法抛出异常,相应的子事务回滚,异常被外围方法感知,外围方法事务回滚,其他子事务即插入“张三”方法事务也回滚了。
*
*/
@Transactional
@Override
public void transaction_nested_nested_exception(){
User1 user1=new User1();
user1.setName("张三");
user1Service.addNested(user1);
User2 user2=new User2();
user2.setName("李四");
user2Service.addNestedException(user2);
}
/**
* 结果:张三(插入),李四(插入)</br>
* 外围方法为开启事务,插入“张三”方法和插入“李四”方法分别开启自己的事务,外围方法事务回滚,所有方法均不回滚。
*
*/
@Override
public void notransaction_exception_nested_nested(){
User1 user1=new User1();
user1.setName("张三");
user1Service.addNested(user1);
User2 user2=new User2();
user2.setName("李四");
user2Service.addNested(user2);
throw new RuntimeException();
}
/**
* 结果:张三(插入),李四(未插入)</br>
* 外围方法开启事务,插入“张三”方法和插入“李四”方法分别开启自己的事务,插入“李四”方法抛出异常,相应的子事务回滚,异常被外围方法感知,外围方法无事务所以无需回滚,故插入“张三”方法没有回滚。
*
*/
@Override
public void notransaction_nested_nested_exception(){
User1 user1=new User1();
user1.setName("张三");
user1Service.addNested(user1);
User2 user2=new User2();
user2.setName("李四");
user2Service.addNestedException(user2);
}
/**
* 结果:张三(插入),李四(未插入)</br>
* 外围方法开启事务,插入“张三”方法和插入“李四”方法为外围方法的子事务,插入“李四”方法抛出异常,相应的子事务回滚,异常被捕获外围方法不可知,故外围方法事务无需回滚。
*
*/
@Transactional
@Override
public void transaction_nested_nested_exception_try(){
User1 user1=new User1();
user1.setName("张三");
user1Service.addNested(user1);
User2 user2=new User2();
user2.setName("李四");
try {
user2Service.addNestedException(user2);
} catch (Exception e) {
System.out.println("方法回滚");
}
}
/**
* 结果:张三(未插入),李四(未插入)</br>
* 外围方法开启事务,插入“张三”、“李四”方法都在外围方法的事务中运行,加入外围方法事务,所以三个方法同一个事务。外围方法或内部方法抛出异常,
* 整个事务全部回滚。虽然我们catch了插入“李四”方法的异常,使异常不会被外围方法感知,但是插入“李四”方法事务被回滚,内部方法外围方法一个事务,所以整体事务被回滚了。
*/
@Transactional
@Override
public void transaction_required_required_exception_try(){
User1 user1=new User1();
user1.setName("张三");
user1Service.addRequired(user1);
User2 user2=new User2();
user2.setName("李四");
try {
user2Service.addRequiredException(user2);
} catch (Exception e) {
System.out.println("方法回滚");
}
}
/**
* 结果:张三(插入),李四(插入)</br>
* 外围方法开启事务,内部方法都在同一事务中,只要不抛出异常,事务就不会回滚。
*/
@Transactional
@Override
public void transaction_noTransaction_noTransaction_exception_try(){
User1 user1=new User1();
user1.setName("张三");
user1Service.add(user1);
User2 user2=new User2();
user2.setName("李四");
try {
user2Service.addException(user2);
} catch (Exception e) {
System.out.println("方法回滚");
}
}
}