事务
业务逻辑上的一组操作,要么全部成功,要么全部失败,比如下面的伪代码:
public class MyService {
//外界调用
public void doService(){
//A逻辑
serviceA();
//B逻辑
serviceB();
//C逻辑
serviceC();
//其他
otherService();
}
}
当外界调用doService方法,该方法里面的各个子逻辑单元,要么全部成功,要么全部失败
事务转账场景分析(事务的传播行为)
事务传播行为:同一个类中相互调用
同一个类AClass中,有两个函数aFunction、aInnerFunction。aFunction调用aInnerFunction。而且aFunction函数会被外部调用。
情况1: aFunction添加了@Transactional注解,aInnerFunction函数没有添加。aInnerFunction抛异常。
结果:两个方法都回滚,aInnerFunction依附在aFunction事务,aInnerFunction抛异常也间接等于aFunction抛异常
public class AClass {
@Transactional(rollbackFor = Exception.class)
public void aFunction() {
//todo: 数据库操作A(增,删,该)
aInnerFunction(); // 调用内部没有添加@Transactional注解的函数
}
private void aInnerFunction() {
//todo: 操作数据B(做了增,删,改 操作)
throw new RuntimeException("函数执行有异常!");
}
}
情况2:两个函数都添加了@Transactional注解。aInnerFunction抛异常。
结果:同第一种情况一样,两个函数对数据库操作都会回滚。因为同一个类中函数相互调用的时候,内部函数添加@Transactional注解无效,也会依附于外部调用的方法事务。@Transactional注解只有外部调用才有效。
public class AClass {
@Transactional(rollbackFor = Exception.class)
public void aFunction() {
//todo: 数据库操作A(增,删,该)
aInnerFunction(); // 调用内部添加@Transactional注解的函数
}
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public void aInnerFunction() {
//todo: 操作数据B(做了增,删,改 操作)
throw new RuntimeException("函数执行有异常!");
}
}
情况3: aFunction不添加注解,aInnerFunction添加注解。aInnerFunction抛异常。
结果:两个函数对数据库的操作都不会回滚。因为内部函数@Transactional注解添加和没添加一样(内部函数添加@Transactional注解无效),两个方法都没事务
public class AClass {
public void aFunction() {
//todo: 数据库操作A(增,删,该)
aInnerFunction(); // 调用内部添加@Transactional注解的函数
}
@Transactional(rollbackFor = Exception.class)
protected void aInnerFunction() {
//todo: 操作数据B(做了增,删,改 操作)
throw new RuntimeException("函数执行有异常!");
}
}
情况4:aFunction添加了@Transactional注解,aInnerFunction函数没有添加。aInnerFunction抛异常,不过在aFunction里面把异常抓出来了。aFunction能够正常往下执行,并没有抛出异常
结果:两个函数里面的数据库操作都成功。事务回滚的动作发生在当有@Transactional注解函数有对应异常抛出时才会回滚。
如果此时aFunction显示抛出异常,就会整体回滚
public class AClass {
@Transactional(rollbackFor = Exception.class)
public void aFunction() {
//todo: 数据库操作A(增,删,该)
try {
aInnerFunction(); // 调用内部没有添加@Transactional注解的函数
} catch (Exception e) {
e.printStackTrace();
}
}
private void aInnerFunction() {
//todo: 操作数据B(做了增,删,改 操作)
throw new RuntimeException("函数执行有异常!");
}
}
事务传播行为:不同类中函数相互调用
两个类AClass、BClass。AClass类有aFunction、BClass类有bFunction。AClass类aFunction调用BClass类bFunction。最终会在外部调用AClass类的aFunction。
情况1:aFunction添加注解,bFunction不添加注解。bFunction抛异常。
结果:两个方法都会回滚,可类比在同类中相互调用的情况1
//类AClass
@Service("aClass")
public class AClass {
@Autowired
private AccountMapperImpl accountMapper;
@Autowired
private BClass bClass;
@Transactional
public void aFunction(){
accountMapper.addMoney();
bClass.bFunction();
}
}
//类BClass
@Service("bClass")
public class BClass {
@Autowired
private AccountMapperImpl accountMapper ;
public void bFunction(){
accountMapper.reduceMoney();
throw new RuntimeException("bFunction函数方法调用异常");
}
}
情况2:aFunction、bFunction两个函数都添加注解,bFunction抛异常。
结果:两个函数对数据库的操作都回滚了。两个函数里面用的还是同一个事务。这种情况下,你可以认为事务rollback了两次。
两个函数都有异常。
//类AClass
@Service("aClass")
public class AClass {
@Autowired
private AccountMapperImpl accountMapper;
@Autowired
private BClass bClass;
@Transactional
public void aFunction(){
accountMapper.addMoney();
bClass.bFunction();
}
}
//类BClass
@Service("bClass")
public class BClass {
@Autowired
private AccountMapperImpl accountMapper ;
@Transactional
public void bFunction(){
accountMapper.reduceMoney();
throw new RuntimeException("bFunction函数方法调用异常");
}
}
情况3:aFunctio不添加注解、bFunction添加注解,bFunction抛异常。
结果:aFunctio方法不回滚,bFunction方法回滚,当前只存在bFunction方法的事务
//类AClass
@Service("aClass")
public class AClass {
@Autowired
private AccountMapperImpl accountMapper;
@Autowired
private BClass bClass;
public void aFunction(){
accountMapper.addMoney();
bClass.bFunction();
}
}
//类BClass
@Service("bClass")
public class BClass {
@Autowired
private AccountMapperImpl accountMapper ;
@Transactional
public void bFunction(){
accountMapper.reduceMoney();
throw new RuntimeException("bFunction函数方法调用异常");
}
}
情况4:aFunction、bFunction两个函数都添加注解,bFunction抛异常。aFunction抓出异常。
结果:两个方法执行不成功,还抛出了org.springframework.transaction.UnexpectedRollbackException:
Transaction rolled back because it has been marked as rollback-only,两个方法处于同一事务,但是bFunction抛出异常,调了事务的rollback函数。
事务被标记了只能rollback了,程序继续执行,aFunction函数里面把异常给抓出来了,
这个时候aFunction函数没有抛出异常,既然你没有异常那事务就需要提交,会调事务的commit函数。
而之前已经标记了事务只能rollback-only(以为是同一个事务)。直接就抛异常了,不让调了。综上,两个方法都会回滚
//类AClass
@Service("aClass")
public class AClass {
@Autowired
private AccountMapperImpl accountMapper;
@Autowired
private BClass bClass;
@Transactional
public void aFunction(){
accountMapper.addMoney();
try {
bClass.bFunction();
} catch (Exception e) {
e.printStackTrace();
}
}
}
//类BClass
@Service("bClass")
public class BClass {
@Autowired
private AccountMapperImpl accountMapper ;
@Transactional
public void bFunction(){
accountMapper.reduceMoney();
throw new RuntimeException("bFunction函数方法调用异常");
}
}
情况5:aFunction、bFunction两个函数都添加注解,bFunction抛异常。aFunction抓出异常。
这里要注意bFunction函数@Transactional注解我们是有变化的,加了一个参数
propagation = Propagation.REQUIRES_NEW,控制事务的传播行为。表明是一个新的事务。
结果:bFunction函数里面的操作回滚了,aFunction里面的操作成功了。因为两者不是在同一个事务中了
//类AClass
@Service("aClass")
public class AClass {
@Autowired
private AccountMapperImpl accountMapper;
@Autowired
private BClass bClass;
@Transactional
public void aFunction(){
//accountMapper.addMoney();如果当前存在事务写操作,会引起当前事务的挂起
try {
bClass.bFunction();
} catch (Exception e) {
e.printStackTrace();
}
}
}
//类BClass
@Service("bClass")
public class BClass {
@Autowired
private AccountMapperImpl accountMapper ;
@Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor = Exception.class)
public void bFunction(){
accountMapper.reduceMoney();
throw new RuntimeException("bFunction函数方法调用异常");
}
}
总结
Spring事务实际业务场景应该就是以上总结,首先要弄清楚事务注解中的各个属性,然后判断是在同一个类中方法相互调用呢还是不同类的方法调用,因为同一个类中函数相互调用的时候,内部函数添加@Transactional注解无效,会依附于外部调用的方法事务。