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能做到, 但它是单独的事务,不与父类一块提交的