一、前置知识
1、Java异常中,Throwable是最顶层的父类,有Error和Exception两个子类
2、Exception分为运行时异常(RuntimeException及其子类)和非运行时异常(Exception子类中,除了RuntimeException及其子类之外的类)
3、使用spring的@Transactiona开启事务,默认Error和RuntimeException及其子类才会回滚
4、@Transactiona默认传播行为是REQUIRED,如需配合其他传播行为测试,请查看:Spring事务传播行为实战
二、情况分析
1、遇到异常可能回滚
1.1、Error和RuntimeException及其子类的异常,数据回滚
// 回滚
@Transactional
public void add() {
KsA ksA = new KsA();
ksA.setName("林");
ksAService.insert(ksA);
throw new RuntimeException("运行报错啦");
}
1.2、非运行时异常,数据不回滚
// 不回滚
@Transactional
public void add2() throws IOException {
KsA ksA = new KsA();
ksA.setName("林");
ksAService.insert(ksA);
throw new IOException();
}
2、异常捕获
2.1、针对当前方法异常捕获
@Transactional
public void add() {
KsA ksA = new KsA();
ksA.setName("林");
ksAService.insert(ksA);
try {
throw new RuntimeException("运行报错啦");
} catch (RuntimeException e) {
e.printStackTrace();
}
}
2.2、针对跨方法异常捕获
针对跨方法捕获的异常,事务回滚,因为内部事务已经结束,确实是抛出了异常
@Transactional
public String insertForlanA(ForlanA forlanA) {
try {
forlanBService.insertForlanB(new ForlanB());
} catch (Exception e) {
e.printStackTrace();
return "特定异常结果";
}
return "成功";
}
@Transactional
public String insertForlanB(ForlanB forlanB) {
forlanBDao.insert(forlanB);
int res = 1 / 0; //java.lang.ArithmeticException: / by zero
return "成功";
}
3、指定异常进行回滚
3.1 RuntimeException及其子类异常
// 回滚
@Transactional(rollbackFor = NullPointerException.class)
public void add() {
KsA ksA = new KsA();
ksA.setName("林");
ksAService.insert(ksA);
int forlan = 1 / 0;
}
这里抛出的是ArithmeticException,但我们指定是NullPointerException才回滚,为什么还是回滚了呢?
其实这两个类都是继承RuntimeException,Spring本来就默认了RuntimeException及其子类也是回滚的
3.2 非运行时异常
我们为什么还要指定rollbackFor参数?我们来看看下面的情况
// 回滚
@Transactional(rollbackFor = IOException.class)
public void add2() throws IOException {
KsA ksA = new KsA();
ksA.setName("林");
ksAService.insert(ksA);
throw new IOException();
}
我们从1.2得知,非运行时异常默认是不回滚,但我们可以通过指定rollbackFor参数来回滚
结论:是针对非运行时异常的,在原基础上拓展
4、指定异常不回滚
// 不回滚
@Transactional(noRollbackFor = ArithmeticException.class)
public void add2() {
KsA ksA = new KsA();
ksA.setName("林");
ksAService.insert(ksA);
int forlan = 1 / 0;
}
5、指定部分代码回滚
// ks_a表数据插入成功,ks_b数据回滚
@Transactional
public void add3() {
KsA ksA = new KsA();
ksA.setName("林");
ksAService.insert(ksA);
Object savepoint = TransactionAspectSupport.currentTransactionStatus().createSavepoint();
try {
KsB ksB = new KsB();
ksB.setAge(10);
ksBService.insert(ksB);
throw new RuntimeException("父方法报错");
} catch (Exception e) {
TransactionAspectSupport.currentTransactionStatus().rollbackToSavepoint(savepoint);
}
}
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/88956.html