spring事务 Propagation.REQUIRES_NEW、Propagation.NESTED、Propagation.REQUIRED使用时候发现的

Spring事务的传播性与隔离级别
    Spring它对JDBC的隔离级别作出了补充和扩展,其提供了7种事务传播行为。
    1、PROPAGATION_REQUIRED:默认事务类型,如果没有,就新建一个事务;如果有,就加入当前事务。适合绝大多数情况。
    2、PROPAGATION_REQUIRES_NEW:如果没有,就新建一个事务;如果有,就将当前事务挂起。
    3、PROPAGATION_NESTED:如果没有,就新建一个事务;如果有,就在当前事务中嵌套其他事务。
    4、PROPAGATION_SUPPORTS:如果没有,就以非事务方式执行;如果有,就使用当前事务。
    5、PROPAGATION_NOT_SUPPORTED:如果没有,就以非事务方式执行;如果有,就将当前事务挂起。即无论如何不支持事务。
    6、PROPAGATION_NEVER:如果没有,就以非事务方式执行;如果有,就抛出异常。
    7、PROPAGATION_MANDATORY:如果没有,就抛出异常;如果有,就使用当前事务。
    第4、5、6、7种特性很好理解了,主要是前三种特性比较容易混淆或用错。

下面我用组合例子做个场景展示。

 

我的数据库有2个表

这个是账户余额表

 

这个是账户余额变更日志表

 

 

第一个例子 2个PROPAGATION_REQUIRED事务

 

 

@Transactional
	public String updateAccountTest(AccountTest accountTest) throws BusinessException {
		String strResult = "true";
		try {
			AccountMessage accountMessage = new AccountMessage();
			accountMessage.setMessage("修改用户" + accountTest.getName() + "金额" + accountTest.getMoney());
			accountMessageBusinessImpl.insertAccountMessageRequired(accountMessage);
			accountMessageBusinessImpl.updateAccountRequired(accountTest);
		} catch (BusinessException e) {
			logger.error("类:AccountTestServiceImpl;方法:updateAccountTest;错误信息:" + e);
			strResult = "false";
			throw e;
		} catch (RuntimeException e) {
			throw e;
		}
		return strResult;

	}

@Transactional(propagation = Propagation.REQUIRED)
	public String updateAccountRequired(AccountTest accountTest) throws BusinessException {
		String strResult = "true";
		try {
			accountTestServiceImpl.updateObject("updateAccountTest", accountTest);
			int i = 1 / 0;// 将发生异常
		} catch (BusinessException e) {
			logger.error("类:AccountTestServiceImpl;方法:updateAccountTest;错误信息:" + e);
			strResult = "false";
			throw e;
		}
		return strResult;
	}

 

 

@Transactional(propagation=Propagation.REQUIRED)
	public String insertAccountMessageRequired(AccountMessage accountMessage) throws BusinessException {
		String strResult = "true";
		try {
			accountMessageServiceImpl.saveObject("insertAccountMessage", accountMessage);
		} catch (BusinessException e) {
			logger.error("类:AccountMessageServiceImpl;方法:insertAccountMessage;错误信息:" + e);
			strResult = "false";
			throw e;
		}
		return strResult;

	}

 

@Test
	public void test() {
		AccountTest accountTest = new AccountTest();
		accountTest.setAge(2);
		accountTest.setId(7);
		accountTest.setMoney(new BigDecimal(150));
		accountTest.setName("test");
		accountTestBusinessImpl.updateAccountTest(accountTest);
	}

 

我设置了一个runtimeException的异常抛出,事务的传播Propagation.REQUIRED,他的特点是如果有事务,就加入当前事务,可以看到这两个方法其实是在一个事务里。
执行updateAccountTest这个方法后,数据库的结果应该是

余额表未更新、账户余额变更日志表未插入

 


第2个例子 PROPAGATION_REQUIRED和PROPAGATION_REQUIRES_NEW组合

 

@Transactional  
	public String updateAccountTest(AccountTest accountTest) throws BusinessException {  
	    String strResult = "true";  
	    try {  
	        AccountMessage accountMessage = new AccountMessage();  
	        accountMessage.setMessage("修改用户" + accountTest.getName() + "金额" + accountTest.getMoney());  
	        accountMessageBusinessImpl.updateAccountRequiresNew(accountTest); //如果updateAccountRequiresNew这个方法在本类定义,this.updateAccountRequiresNew(accountTest),结果又会是怎么样呢
	        accountMessageBusinessImpl.insertAccountMessageRequired(accountMessage);  
	    } catch (BusinessException e) {  
	        logger.error("类:AccountTestServiceImpl;方法:updateAccountTest;错误信息:" + e);  
	        strResult = "false";  
	        throw e;  
	    } catch (RuntimeException e) {  
	        throw e;  
	    }  
	    return strResult;  
	}  

 

 

 

 

 

 

@Transactional(propagation=Propagation.REQUIRED)
	public String insertAccountMessageRequired(AccountMessage accountMessage) throws BusinessException {
		String strResult = "true";
		try {
			accountMessageServiceImpl.saveObject("insertAccountMessage", accountMessage);
			int i=1/0;
		} catch (BusinessException e) {
			logger.error("类:AccountMessageServiceImpl;方法:insertAccountMessage;错误信息:" + e);
			strResult = "false";
			throw e;
		}
		return strResult;
	}
@Transactional(propagation=Propagation.REQUIRES_NEW)
	public String updateAccountRequiresNew(AccountTest accountTest) throws BusinessException {
		String strResult = "true";
		try {
			accountTestServiceImpl.updateObject("updateAccountTest", accountTest);
		} catch (BusinessException e) {
			logger.error("类:AccountTestServiceImpl;方法:updateAccountTest;错误信息:" + e);
			strResult = "false";
			throw e;
		}
		return strResult;
	}

 

 

 

@Test
	public void test() {
		AccountTest accountTest = new AccountTest();
		accountTest.setAge(2);
		accountTest.setId(7);
		accountTest.setMoney(new BigDecimal(150));
		accountTest.setName("test");
		accountTestBusinessImpl.updateAccountTest(accountTest);
	}

 

 

 

 

 

 

 

这个时候的结果是 余额表更新了

账户余额变更日志表没变


看方法的执行顺序,先执行了updateAccountRequiresNew ,再执行insertAccountMessageRequired,
异常是在insertAccountMessageRequired抛出的,因为updateAccountRequiresNew这个方法新建了一个事务,他的方法执行完就commit。insertAccountMessageRequired这个rollback了。

 


第3个例子 PROPAGATION_NESTED嵌套事务
他的特点是
                1.联合成功(PROPAGATION_REQUIRED的特性) 
2.隔离失败(PROPAGATION_REQUIRES_NEW的特性)。
在测试PROPAGATION_NESTED 之前时候我先用PROPAGATION_REQUIRED演示一下

 

 

@Transactional
	public String updateAccountTest(AccountTest accountTest) throws BusinessException {
		String strResult = "true";
		try {
			AccountMessage accountMessage = new AccountMessage();
			accountMessage.setMessage("修改用户" + accountTest.getName() + "金额" + accountTest.getMoney());
			try {
				accountMessageBusinessImpl.insertAccountMessageRequired(accountMessage);
			} catch (RuntimeException e) {
				accountMessageBusinessImpl.updateAccountRequired(accountTest);
				throw e; // 如果注释 Transaction rolled back because it has been marked as rollback-only。

			}
		} catch (BusinessException e) {
			logger.error("类:AccountTestServiceImpl;方法:updateAccountTest;错误信息:" + e);
			strResult = "false";
			throw e;
		} catch (RuntimeException e) {
			throw e;
		}
		return strResult;
	}

	@Transactional(propagation=Propagation.REQUIRED)
	public String updateAccountRequired(AccountTest accountTest) throws BusinessException {
		String strResult = "true";
		try {
			accountTestServiceImpl.updateObject("updateAccountTest", accountTest);
		} catch (BusinessException e) {
			logger.error("类:AccountTestServiceImpl;方法:updateAccountTest;错误信息:" + e);
			strResult = "false";
			throw e;
		}
		return strResult;
	}
@Transactional(propagation=Propagation.REQUIRED)
	public String insertAccountMessageRequired(AccountMessage accountMessage) throws BusinessException {
		String strResult = "true";
		try {
			accountMessageServiceImpl.saveObject("insertAccountMessage", accountMessage);
			int i=1/0;
		} catch (BusinessException e) {
			logger.error("类:AccountMessageServiceImpl;方法:insertAccountMessage;错误信息:" + e);
			strResult = "false";
			throw e;
		}
		return strResult;
	}

 

@Test
	public void test() {
		AccountTest accountTest = new AccountTest();
		accountTest.setAge(2);
		accountTest.setId(7);
		accountTest.setMoney(new BigDecimal(180));
		accountTest.setName("test");
		accountTestBusinessImpl.updateAccountTest(accountTest);
	}


为了区分这次将余额修改成180

 

结果和上次比并没有变化

余额表没有更新

日志表未添加记录

 

 

 

 

PROPAGATION_NESTED 开始一个 "嵌套的" 事务,  它是已经存在事务的一个真正的子事务. 潜套事务开始执行时,  它将取得一个 savepoint. 如果这个嵌套事务失败, 我们将回滚到此 savepoint. 潜套事务是外部事务的一部分, 只有外部事务结束后它才会被提交. 

现在可以测试错误隔离了

 

@Transactional
	public String updateAccountTest(AccountTest accountTest) throws BusinessException {
		String strResult = "true";
		try {
			AccountMessage accountMessage = new AccountMessage();
			accountMessage.setMessage("修改用户" + accountTest.getName() + "金额" + accountTest.getMoney());
			try{
				accountMessageBusinessImpl.insertAccountMessageNested(accountMessage);
			}catch(RuntimeException e){
				accountMessageBusinessImpl.updateAccountRequired(accountTest);
			}
		} catch (BusinessException e) {
			logger.error("类:AccountTestServiceImpl;方法:updateAccountTest;错误信息:" + e);
			strResult = "false";
			throw e;
		} catch (RuntimeException e) {
			throw e;
		}
		return strResult;
	}

 

@Transactional(propagation=Propagation.NESTED)
	public String insertAccountMessageNested(AccountMessage accountMessage) throws RuntimeException {
		String strResult = "true";
		try {
			accountMessageServiceImpl.saveObject("insertAccountMessage", accountMessage);
			int i=1/0;
		} catch (BusinessException e) {
			logger.error("类:AccountMessageServiceImpl;方法:insertAccountMessage;错误信息:" + e);
			strResult = "false";
			throw e;
		}catch(RuntimeException e){
			throw e;
		}
		return strResult;
	}
@Transactional(propagation=Propagation.REQUIRED)
	public String updateAccountRequired(AccountTest accountTest) throws BusinessException {
		String strResult = "true";
		try {
			accountTestServiceImpl.updateObject("updateAccountTest", accountTest);
		} catch (BusinessException e) {
			logger.error("类:AccountTestServiceImpl;方法:updateAccountTest;错误信息:" + e);
			strResult = "false";
			throw e;
		}
		return strResult;
	}

 

	@Test
	public void test() {
		AccountTest accountTest = new AccountTest();
		accountTest.setAge(2);
		accountTest.setId(7);
		accountTest.setMoney(new BigDecimal(180));
		accountTest.setName("test");
		accountTestBusinessImpl.updateAccountTest(accountTest);
	}



结果是日志表未插入新纪录

 

余额表发生更新

 





由此可见, PROPAGATION_REQUIRES_NEW 和 PROPAGATION_NESTED 的最大区别在于,PROPAGATION_REQUIRES_NEW 完全是一个新的事务, 而 PROPAGATION_NESTED则是外部事务的子事务, 如果外部事务 commit, 潜套事务也会被 commit,这个规则同样适用于 roll back.

 

NESTED的具体用途如下:在此方法出现异常时,通过TRY CATCH 代码块包含住, 继续往下执行或者执行CATCH中的处理.此点REUQIRED做不到, REQUIRED_NEW能做到, 但它是单独的事务,不与父类一块提交的

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值