spring-事务源码解析(三)

8 篇文章 0 订阅
3 篇文章 0 订阅

前面spring-事务源码解析(一)spring-事务源码解析(二)两篇文章分别介绍了spring容器如何对bean进行事务代理和具体事务代理逻辑以及如何和我们自己使用的JDBC框架(以JdbcTemplate为例)一起集成的,今天来说一下spring事务的传播机制,这点无论是在面试中还是自己工作中都是很重要的部分。

1. spring事务提供的传播机制枚举

/**
 * 如果有事务,就加入事务,没有的话就新建一个(默认)
 */
REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED),

/**
 *支持当前事务,如果当前没有事务,就以非事务方式执行
 */
SUPPORTS(TransactionDefinition.PROPAGATION_SUPPORTS),

/**
 * 使用当前的事务,如果当前没有事务,就抛异常
 */
MANDATORY(TransactionDefinition.PROPAGATION_MANDATORY),

/**
 * 不管是否存在事务,都创建一个新的事务,原来的事务挂起,新的执行完毕,继续执行老的事务
 */
REQUIRES_NEW(TransactionDefinition.PROPAGATION_REQUIRES_NEW),

/**
 * 以非事务方式执行,如果当前存在事务,就把当前事务挂起
 */
NOT_SUPPORTED(TransactionDefinition.PROPAGATION_NOT_SUPPORTED),

/**
 *以非事务方式执行,如果当前存在事务,则抛异常(与MANDATORY相反)
 */
NEVER(TransactionDefinition.PROPAGATION_NEVER),

/**
 * 如果当前存在事务,则在嵌套事务内执行,如果当前没有事务,则执行与REQUIRED类似的操作
 */
NESTED(TransactionDefinition.PROPAGATION_NESTED);

 2.测试

@Repository
public class UserDaompl implements IUserDao {

	@Autowired
	private JdbcTemplate jdbcTemplate;
	
	@Transactional
	@Override
	public int insertRequired(String name) {
		return jdbcTemplate.update("insert into user(name) values(?)", name);
	}

	@Transactional(propagation=Propagation.REQUIRES_NEW)
	@Override
	public int insertRequiresNew(String name) {
		return jdbcTemplate.update("insert into user(name) values(?)", name);
	}

	@Transactional(propagation=Propagation.NESTED)
	@Override
	public int insertNested(String name) {
		return jdbcTemplate.update("insert into user(name) values(?)", name);
	}
}

@Repository
public class User2DaoImpl implements IUser2Dao {

	@Autowired
	private JdbcTemplate jdbcTemplate;
    
        @Override
	public int insertException(String name) {
		jdbcTemplate.update("insert into user2(name) values(?)", name);
		throw new IllegalArgumentException();
	}
	
	@Transactional
	@Override
	public int insertRequired(String name) {
		return jdbcTemplate.update("insert into user2(name) values(?)", name);
	}

	@Transactional
	@Override
	public int insertRequiredException(String name) {
		jdbcTemplate.update("insert into user2(name) values(?)", name);
		throw new IllegalArgumentException();
	}

	@Transactional(propagation=Propagation.REQUIRES_NEW)
	@Override
	public int insertRequiresNew(String name) {
		return jdbcTemplate.update("insert into user2(name) values(?)", name);
	}

	@Transactional(propagation=Propagation.REQUIRES_NEW)
	@Override
	public int insertRequiresNewException(String name) {
		jdbcTemplate.update("insert into user2(name) values(?)", name);
		throw new IllegalArgumentException();
	}

	@Transactional(propagation=Propagation.NESTED)
	@Override
	public int insertNested(String name) {
		return jdbcTemplate.update("insert into user2(name) values(?)", name);
	}

	@Transactional(propagation=Propagation.NESTED)
	@Override
	public int insertNestedException(String name) {
		jdbcTemplate.update("insert into user2(name) values(?)", name);
		throw new IllegalArgumentException();
	}

}

@Service
public class UserServiceImpl implements IUserService{

	@Autowired
	private IUserDao userDao;
	
	@Autowired
	private IUser2Dao user2Dao;
	
	/********************** required *******************************/


	@Transactional
	@Override
	public void notTransaction__required_not_transaction_exception() {
		// 会回滚
		user2Dao.insertException("李四");
		// 不会回滚
    //		try {
    //			user2Dao.insertException("李四");
    //		} catch (Exception e) {
    //			System.out.println("方法回滚");
    //		}
	}
	
	/**
	 * 张三、李四都可以插入
	 * 外围方法未开启事务,内部两个方法都在自己的事务中独立运行,外围方法异常不影响内部方法独立的事务
	 */
	@Override
	public void notTransaction_exception_required_required() {
		userDao.insertRequired("张三");
		user2Dao.insertRequired("李四");
		throw new IllegalArgumentException();
	}

	/**
	 * 张三可以插入, 李四不能插入
	 * 外围方法没有事务,内部方法都在自己的事务中独立运行,所以方法2抛出异常只会回滚自己的事务,不会影响方法1的事务
	 */
	@Override
	public void notTransaction_required_required_exception() {
		userDao.insertRequired("张三");
		user2Dao.insertRequiredException("李四");
	}

	/**
	 * 张三、李四都未插入
	 * 外围方法开启事务,内部方法加入外围方法事务,外围方法回滚,内部方法事务也会回滚
	 */
	@Transactional
	@Override
	public void transaction_exception_required_required() {
		userDao.insertRequired("张三");
		user2Dao.insertRequired("李四");
		throw new IllegalArgumentException();
	}

	/**
	 * 张三,李四均为插入
	 * 外部方法开启事务,内部方法加入外部方法事务,内部方法抛出异常,外部方法感知到异常导致整体事务回滚
	 */
	@Transactional
	@Override
	public void transaction_required_required_exception() {
		userDao.insertRequired("张三");
		user2Dao.insertRequiredException("李四");
	}

	/**
	 * 张三,李四均为插入
	 * 外部方法开启事务,内部方法加入外部方法事务,内部方法抛出异常会标记当前事务为rollback-only,
	 * 即使异常在外部方法中被catch了,外部方法事务不会检测到异常,但会检测到rollback-only,从而导致整体事务回滚
	 */
	@Transactional
	@Override
	public void transaction_required_required_exception_try() {
		userDao.insertRequired("张三");
		try {
			user2Dao.insertRequiredException("李四");
		} catch (Exception e) {
			System.out.println("方法回滚");
		}
	}

	/********************** requires_new *******************************/
	
	/**
	 * 张三,李四均插入
	 * 外部方法没有事务,内部方法都在自己的事务中独立运行,外部方法抛出异常不会影响内部方法
	 */
	@Override
	public void notTransaction_exception_requiresNew_requiresNew() {
		userDao.insertRequiresNew("张三");
		user2Dao.insertRequiresNew("李四");
		throw new IllegalArgumentException();
	}

	/**
	 * 张三插入,李四未插入
	 * 外部方法没有开始事务,内部方法运行在自己独立事务内,插入李四方法抛出异常回滚,其它事务不受影响
	 */
	@Override
	public void notTransaction_requiresNew_requiresNew_exception() {
		userDao.insertRequiresNew("张三");
		user2Dao.insertRequiresNewException("李四");
	}

	/**
	 * 张三未插入,李四插入,王五插入
	 * 外部方法开启事务,插入张三方法使用外部方法事务,插入李四和王五的方法都运行在各自独立事务内,
	 * 外部方法抛异常回滚只会回滚同一事务的方法,所以,插入张三的方法会一同回滚
	 */
	@Transactional
	@Override
	public void transaction_exception_required_requiresNew_requiresNew() {
		userDao.insertRequired("张三");
		user2Dao.insertRequiresNew("李四");
		user2Dao.insertRequiresNew("王五");
		throw new IllegalArgumentException();
	}

	/**
	 * 张三未插入,李四插入,王五未插入
	 * 外部方法开启事务,插入张三方法使用外部方法事务,插入李四和王五的方法都运行在各自独立事务内,
	 * 插入王五方法抛出异常,首先自己的事务会回滚,随后,异常继续抛出被外部方法感知,外部方法事务会被回滚,
	 * 古插入张三的方法也会随之回滚
	 */
	@Transactional
	@Override
	public void transaction_required_requiresNew_requiresNew_exception() {
		userDao.insertRequired("张三");
		user2Dao.insertRequiresNew("李四");
		user2Dao.insertRequiresNewException("王五");
	}

	/**
	 * 张三插入,李四插入,王五未插入
	 * 外部方法开启事务,插入张三方法和外部方法共用同一个事务,插入李四和王五的方法都运行在各自独立事务内,
	 *  插入王五方法抛出异常,首先自己的事务会回滚,随后,异常外部方法catch,不会被外围事务感知到,
	 *  所以,外部方法事务不会回滚,插入张三方法会插入成功
	 */
	@Transactional
	@Override
	public void transaction_required_requiresNew_requiresNew_exception_try() {
		userDao.insertRequired("张三");
		user2Dao.insertRequiresNew("李四");
		try {
			user2Dao.insertRequiresNewException("王五");
		} catch (Exception e) {
			System.out.println("事务回滚");
		}
	}

	/********************** nested *******************************/
	
	/**
	 * 张三,李四均插入
	 * 外围方法未开启事务,内部方法均运行在各自独立事务中,外部方法异常不影响内部方法事务
	 */
	@Override
	public void notTransaction_exception_nested_nested() {
		userDao.insertNested("张三");
		user2Dao.insertNested("李四");
		throw new IllegalArgumentException();
	}

	/**
	 * 张三插入,李四未插入
	 * 外部方法未开启事务,内部方法均运行在各自独立事务中,插入李四方法抛出异常,只会让自己事务回滚,
	 * 虽然向外部方法抛出了异常,但不会影响插入张三方法事务
	 */
	@Override
	public void notTransaction_nested_nested_exception() {
		userDao.insertNested("张三");
		user2Dao.insertNestedException("李四");
	}

	/**
	 * 张三,李四均未插入
	 * 外部方法开启事务,内部方法事务是外部事务的子事务(savePoint),外部方法事务回滚,子事务也会随之回滚
	 */
	@Transactional
	@Override
	public void transaction_exception_nested_nested() {
		userDao.insertNested("张三");
		user2Dao.insertNested("李四");
		throw new IllegalArgumentException();
	}

	/**
	 * 张三,李四均未插入
	 * 外围方法开启事务,内部方法事务是外部事务的子事务,内部方法抛异常回滚,且外部事务感知到异常会导致整体事务的回滚
	 */
	@Transactional
	@Override
	public void transaction_nested_nested_exception() {
		userDao.insertNested("张三");
		user2Dao.insertNestedException("李四");
	}

	/**
	 * 张三插入,李四未插入
	 * 外部方法开启事务,内部事务为外部事务子事务,插入李四方法抛异常回滚,且异常被外部方法catch,不会被外部事务感知,所以整体事务不会回滚
	 */
	@Transactional
	@Override
	public void transaction_nested_nested_exception_try() {
		userDao.insertNested("张三");
		try {
			user2Dao.insertNestedException("李四");
		} catch (Exception e) {
			System.out.println("事务回滚");
		}
	}
	
	/**
	 * 张三插入,李四插入(事务回滚失效)
	 * 外部方法开启事务,插入李四方法为外部事务子事务,这个方法抛出异常,按道理是应该回滚的,但实际并非如此,和transaction_nested_nested_exception_try比较一下
	 */
	@Transactional
	@Override
	public void transaction_fail() {
		user2Dao.insertRequired("张三");
		try {
			user2Dao.insertNestedException("李四");
		} catch (Exception e) {
			System.out.println("事务回滚");
		}
	}
	
	/**
	 * 张三插入,李四未插入(事务回滚生效)
	 * 根本原因和事务没有关系,是动态代理导致的
	 */
	@Transactional
	@Override
	public void transaction_success() {
		user2Dao.insertRequired("张三");
		try {
			// 该用法需要@EnableAspectJAutoProxy(exposeProxy=true)注解支持
			((IUserService)AopContext.currentProxy()).insertNestedException("李四");
		} catch (Exception e) {
			System.out.println("事务回滚");
		}
	}
	
	@Transactional(propagation=Propagation.NESTED)
	@Override
	public int insertNestedException(String name) {
		jdbcTemplate.update("insert into user2(name) values(?)", name);
		throw new IllegalArgumentException();
	}

}


@RunWith(SpringRunner.class)
@SpringBootTest
public class UserTests {
	
	@Autowired
	private IUserService userService;
	
	@Before
	public void setUp() {}
	
	@Test
	public void test() {
		userService.notTransaction_exception_required_required();
	}
}

 上面只对required,requires_new,nested三种传播机制进行了测试,测试结果标记在了对应的方法上,其它的传播机制,感兴趣的同学可以自己动手测试

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值