为什么使用@Transactional时灵时不灵?
同步线程操作
同步示例1
当前类里加上@Transactional注解,如果出现异常,插入操作回滚
@Transactional(rollbackFor = RuntimeException.class)
public int outInsert() {
AnimalPO po = new AnimalPO();
po.setName("示范点");
po.setAge(11);
int insert = animalMapper.insert(po);
//手动抛出异常
int a = 10/ 0;
return insert;
}
同步示例2
当前类里添加编程事务,如果出现异常,事务会回滚
public int outInsert() {
AnimalPO po = new AnimalPO();
po.setName("示范点");
po.setAge(11);
int insert = 0;
//开启事务
TransactionStatus begin = transactionConfig.begin();
try {
insert = animalMapper.insert(po);
//提交事务
transactionConfig.commit(begin);
int a = 10/ 0;
} catch (Exception e) {
transactionConfig.rollback(begin);
log.info("回滚操作",e);
}
return insert;
}
同步示例3
外层service方法里使用注解事务,然后通过内层service插入数据,如果内层service出现异常,那么外层和内层都会回滚。
//外层service
@Transactional(rollbackFor = RuntimeException.class)
public int outInsert() {
//内层sevice插入数据
insert = lionService.intimalInsert();
return insert;
}
//内层service
public int intimalInsert() {
log.info("内层测试开始");
AnimalPO po = new AnimalPO();
po.setName("777");
po.setAge(11);
int insert = animalMapper.insert(po);
int a = 10/ 0;
return insert;
}
同步示例4
- 外层服务添加事务注解,然后先插入数据
- 调用内层服务插入数据,编程事务和注解事务会管理内层服务,但是编程事务优先于注解事务。
- 此时外层服务手动抛出异常,内层服务回滚数据,外层服务回滚数据
- 外层服务注解事务生效,编程事务生效,注解事务包裹住了编程事务
- 手动异常出现在已完成内层service之后,此时内层serivce已完成自己的操作。但是数据库并没有内层插入的数据,因为外层注解事务还没跑完。
- 如果把外层编程式事务放入内层执行,效果和上面的执行是一样的
@Transactional(rollbackFor = RuntimeException.class)
public int outInsert() {
AnimalPO po = new AnimalPO();
po.setName("示范点99999");
po.setAge(11);
animalMapper.insert(po);
int insert = 0;
TransactionStatus begin = transactionConfig.begin();
try {
//内层sevice插入数据
insert = lionService.intimalInsert();
transactionConfig.commit(begin);
} catch (Exception e) {
log.info("进入编程事务catch操作");
transactionConfig.rollback(begin);
}
//抛出异常
int a = 10 / 0;
return insert;
}
//内层service
public int intimalInsert() {
log.info("内层测试开始");
AnimalPO po = new AnimalPO();
po.setName("777");
po.setAge(11);
int insert = animalMapper.insert(po);
return insert;
}
- 如果把异常放在这里执行,效果和6一样,但是错误会多出一个Transaction rolled back because it has been marked as rollback-only,它表示事务多次回滚的调用。
@Transactional(rollbackFor = RuntimeException.class)
public int outInsert() {
AnimalPO po = new AnimalPO();
po.setName("示范点99999");
po.setAge(11);
animalMapper.insert(po);
int insert = 0;
TransactionStatus begin = transactionConfig.begin();
try {
insert = lionService.intimalInsert();
//放在这里
int a = 10 / 0;
transactionConfig.commit(begin);
} catch (Exception e) {
log.info("进入编程事务catch操作");
transactionConfig.rollback(begin);
}
return insert;
}
同步示例5
外层serivce开启注解事务,并插入数据,插入操作被try-catch包裹,此时手动异常,数据正常插入成功,事务不起效果,因为异常被catch吃掉了,原因catch没有抛出异常,如果抛出异常,会被事务捕获,并处理回滚.
@Transactional(rollbackFor = RuntimeException.class)
public int outInsert() {
AnimalPO po = new AnimalPO();
po.setName("示范点99999");
po.setAge(11);
try {
animalMapper.insert(po);
//手动异常
int a = 10 / 0;
} catch (Exception e) {
log.info("catch操作");
}
return 1;
}
同步示例6
同步操作中,本类方法调用,调用的方法加了事务注解,被调用的方法没加,并且是private修饰的,那么数据会被事务管理,如果被调用的类插入数据失败,会导致事务回滚。
@Transactional(rollbackFor = RuntimeException.class)
public int outInsert() {
this.des(22);
return 1;
}
private int des(int a){
AnimalPO po = new AnimalPO();
po.setName("示范点" + a);
po.setAge(11);
animalMapper.insert(po);
int b = 10/0;
return 1;
}
异步线程操作
异步示例1
外层serivce开启注解事务,并插入数据,内层service使用子线程插入数据,内层出现异常,内层插入操作会回滚,外层事务不受干扰,外层插入数据成功。
//添加事务注解即可
@Transactional(rollbackFor = RuntimeException.class)
public int outInsert() {
AnimalPO po = new AnimalPO();
po.setName("示范点99999");
po.setAge(11);
animalMapper.insert(po);
//创建线程
Thread thread = new Thread(() -> {
TransactionStatus begin = transactionConfig.begin();
try {
lionService.intimalInsert();
//手动异常
int a = 10 / 0;
transactionConfig.commit(begin);
} catch (Exception e) {
log.info("进入编程事务catch操作");
transactionConfig.rollback(begin);
}
});
thread.start();
return 1;
}
//内层service
public int intimalInsert() {
log.info("内层测试开始");
AnimalPO po = new AnimalPO();
po.setName("777");
po.setAge(11);
int insert = animalMapper.insert(po);
return insert;
}
异步示例2
外层serivce开启注解事务,并插入数据,内层service使用编程事务,且使用子线程插入数据,此时外层出现异常,外层插入操作会回滚,内层事务不受干扰,内层插入数据成功。
@Transactional(rollbackFor = RuntimeException.class)
public int outInsert() throws InterruptedException {
AnimalPO po = new AnimalPO();
po.setName("示范点99999");
po.setAge(11);
animalMapper.insert(po);
//创建线程
Thread thread = new Thread(() -> {
TransactionStatus begin = transactionConfig.begin();
try {
lionService.intimalInsert();
transactionConfig.commit(begin);
} catch (Exception e) {
log.info("进入编程事务catch操作");
transactionConfig.rollback(begin);
throw new RuntimeException(e);
}
});
thread.start();
//手动异常
int a = 10 / 0;
return 1;
}
//内层service
public int intimalInsert() {
log.info("内层测试开始");
AnimalPO po = new AnimalPO();
po.setName("777");
po.setAge(11);
int insert = animalMapper.insert(po);
return insert;
}
异步示例3
外层serivce开启注解事务,并插入数据。内层没有事务,内层service使用子线程插入数据,此时外层出现异常,外层插入操作会回滚,内层事务不受干扰,内层插入数据成功。内层service不受外层注解事务的管理。
@Transactional(rollbackFor = RuntimeException.class)
public int outInsert() throws InterruptedException {
AnimalPO po = new AnimalPO();
po.setName("示范点99999");
po.setAge(11);
animalMapper.insert(po);
//创建线程
Thread thread = new Thread(() -> {
lionService.intimalInsert();
});
thread.start();
//手动异常
int a = 10 / 0;
return 1;
}
异步示例4
当前方法中,通过本类方法调用本类中带有事务的方法,不会触发注解事务,哪怕调用的方法本身没有事务,也不会触发。以下结果是同步操作和异步操作都插入数据成功
public int outInsert() {
//创建线程
Thread thread = new Thread(() -> {
this.des(12);
});
thread.start();
this.dem(22);
return 1;
}
@Transactional(rollbackFor = RuntimeException.class)
public int dem(int a){
AnimalPO po = new AnimalPO();
po.setName("示范点" + a);
po.setAge(11);
animalMapper.insert(po);
//手动异常
int b = 10/0;
return 1;
}
可以更改为:
public int outInsert() {
//
//AnimalPO po = new AnimalPO();
//po.setName("示范点99999");
//po.setAge(11);
//animalMapper.insert(po);
//创建线程
Thread thread = new Thread(() -> {
this.des(12);
});
thread.start();
this.des(22);
return 1;
}
private int des(int a){
//手动开启事务
TransactionStatus begin = transactionConfig.begin();
try {
AnimalPO po = new AnimalPO();
po.setName("示范点" + a);
po.setAge(11);
animalMapper.insert(po);
int b = 10/0;
//提交
transactionConfig.commit(begin);
} catch (Exception e) {
//回滚
transactionConfig.rollback(begin);
}
return 1;
}
总结
- 同步操作
- 外层添加事务,内层也添加事务.外层事务会管理内层事务,无论哪层事务抛出异常,两边都会回滚数据。
- 外层添加事务,内层添加事务,内层插入手动异常,但是并没有写回滚操作,也没有抛出异常,此时内层和外层插入操作成功。
- 外层有事务,内层没有,内层出现异常时,内层插入操作被try-catch捕获,如果catch没有抛出异常,那么外层事务并不会回滚,因为异常已经被吃掉了。要被外层事务捕获,内层的catch必须抛出异常.
- 本类中,同步调用本类带有注解事务的方法,不会触发事务特性,必须使用编程事务才行。
- 异步操作
- 外层事务和内层事务其实是两个独立的操作,无论哪一边出现异常,只会回滚自己的,并不会影响对方的操作。
- 本类中,异步调用本类带有注解事务的方法,不会触发事务特性,必须使用编程事务才行。
转载自:https://juejin.cn/post/7126108674098987016