Spring中事务失效的场景有哪些?
异常捕获处理
@Transactional
public void update(Integer from, Integer to, Double money) {
try {
//转账的用户不能为空
Account fromAccount = accountDao.selectById(from);
//判断用户的钱是否够转账
if (fromAccount.getMoney() - money >= 0) {
fromAccount.setMoney(fromAccount.getMoney() - money);
accountDao.updateById(fromAccount);
//异常
int a = 1/0;
//被转账的用户
Account toAccount = accountDao.selectById(to);
toAccount.setMoney(toAccount.getMoney() + money);
accountDao.updateById(toAccount);
}
} catch (Exception e) {
e.printStackTrace();
}
}
原因
事务通知只有捉到了目标抛出的异常,才能进行后续的回滚处理,如果目标自己处理掉异常,事务通知无法知悉
解决方案
在catch块添加throw new RuntimeException(e)抛出
为什么在事务处理中,通常建议选择抛出异常而不是捕获异常
事务是用于确保一组数据库操作的一致性和完整性的机制。在事务处理过程中,如果出现异常情况,为了保证事务的一致性,应该将异常抛出,以便事务管理器能够捕获并进行相应的回滚操作。只有将异常抛出,事务管理器才能知道出现了异常情况,从而能够及时回滚事务,保证数据的完整性。
另外,事务管理器通常会将发生的异常转化为特定的事务异常,以便上层的业务逻辑能够更好地处理和反馈异常信息。
但是并不是所有的异常都应该抛出,有些异常是可以在业务代码中捕获并进行适当的处理的。例如,可以在业务层捕获一些已知的业务异常,并根据具体情况进行处理,而不需要对整个事务进行回滚。
总结起来,事务管理中,尽量选择抛出异常,以便事务管理器能够捕获并处理异常,并回滚事务以保证数据的一致性和完整性。但并不是所有的异常都应该抛出,有些异常可以在业务层进行捕获和处理,避免不必要的回滚操作。
抛出检查异常
@Transactional
public void update(Integer from, Integer to, Double money) throws FileNotFoundException {
//转账的用户不能为空
Account fromAccount = accountDao.selectById(from);
//判断用户的钱是否够转账
if (fromAccount.getMoney() - money >= 0) {
fromAccount.setMoney(fromAccount.getMoney() - money);
accountDao.updateById(fromAccount);
//读取文件
new FileInputStream("dddd");
//被转账的用户
Account toAccount = accountDao.selectById(to);
toAccount.setMoney(toAccount.getMoney() + money);
accountDao.updateById(toAccount);
}
}
原因
Spring 默认只会回滚非检查异常
解决方法
配置rollbackFor属性
@Transactional(rollbackFor=Exception.class)
检查异常和非检查异常
Java中的异常可以分为两种类型:非检查异常(Unchecked Exception)和检查异常(Checked Exception)。
-
非检查异常(Unchecked Exception):
非检查异常是指继承自RuntimeException的异常以及其子类,它们在代码编译阶段不需要显式地声明或捕获。非检查异常通常是由程序错误或逻辑问题引起的,例如NullPointerException、IllegalArgumentException等。这些异常在运行时被抛出,并且程序可以选择捕获并处理,但并不强制要求进行处理。通常情况下,非检查异常会导致程序的终止,并且需要进行修复。 -
检查异常(Checked Exception):
检查异常是指继承自Exception的异常以及其子类,但不是继承自RuntimeException的异常。检查异常在代码编译阶段要求必须显式地声明或捕获。这些异常通常是由外部因素或条件引起的,例如I/O错误、网络连接问题等。程序在编译时必须捕获并处理这些异常,以确保异常情况得到妥善处理。如果没有捕获或声明抛出这些异常,编译器会报错。因此,检查异常会在编码过程中强制程序员进行处理,确保代码能够在异常情况下正常运行或进行特定的处理。
需要注意的是,RuntimeException及其子类是非检查异常,其他继承自Exception的异常是检查异常。
总结起来,非检查异常在编译时不强制要求处理,并且通常是程序错误或逻辑问题引起的;检查异常在编译时要求必须显式处理,并且通常是由外部条件或因素引起的。在实际编码中,应根据具体情况选择适当的异常类型并进行处理。
非public方法
当我们把上面代码的public删掉
原因
Spring 为方法创建代理、添加事务通知、前提条件都是该方法是 public 的
解决方法
改为 public 方法
面试回答
面试官:Spring中事务失效的场景有哪些
候选人: 嗯!这个在项目中之前遇到过,第一个,如果方法上异常捕获处理,自己处理了异常,没有抛出,就会导致 事务失效,所以一般处理了异常以后,别忘了跑出去就行了
第二个,如果方法抛出检查异常,如果报错也会导致事务失效,最后在 spring事务的注解上,就是@Transactional上配置rollbackFor属性为 Exception,这样别管是什么异常,都会回滚事务
第三,我之前还遇到过一个,如果方法上不是public修饰的,也会导致事务 失效 嗯,就能想起来那么多