1. 事务传播机制
PROPAGATION_REQUIRED | 默认传播机制如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务 | 无父事务时:子事务作为独立事务执行;有父事务时:子事务中的操作串入父事务中执行,并且一起提交,一个操作失败全部回滚 |
---|---|---|
PROPAGATION_SUPPORTS | 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行 | 无父事务时:以非事务方式执行;有父事务时:加入父事务执行,等同于PROPAGATION_REQUIRED |
PROPAGATION_MANDATORY | 如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常 | 无父事务时:抛出异常;有父事务时:加入父事务执行,等同于PROPAGATION_REQUIRED |
PROPAGATION_REQUIRES_NEW | 创建一个新的事务,如果当前存在事务,则把当前事务挂起 | 无父事务时:子事务新建事务作为独立事务执行;有父事务时:子事务新建事务作为独立事务执行,独立提交; |
NOT_SUPPORTED | 以非事务方式运行,如果当前存在事务,则把当前事务挂起 | 无父事务时:以非事务方式执行;有父事务时:挂起父事务,自己按照无事务方式运行;子事务自身无回滚,出现异常若向上抛,可能导致父事务回滚;父事务回滚时,不会影响子事务 |
NEVER | 以非事务方式运行,如果当前存在事务,则抛出异常 | 无父事务时:以非事务方式执行;有父事务时:抛出异常(若不处理会导致父事务回滚) |
NESTED | 如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行 | 无父事务时:创建独立事务,等同于PROPAGATION_REQUIRED;有父事务时:嵌套在父事务之内;子事务依赖父事务:子事务于父事务提交时提交;父事务回滚,子事务也回滚。 |
2. 同类中A方法调用B方法
- A方法有事务,B方法没事务
@Service
public class DaServiceImpl extends ServiceImpl<DaDao, Da> implements DaService {
@Transactional
@Override
public void insertEntity() {
Da da = new Da();
da.setCode("测试1");
da.setDrawingCode("图片地址1");
this.save(da);
updateEntity(da);
}
public void updateEntity(Da dd){
dd.setDrawingCode("修改地址");
this.updateById(dd);
int sum =1/0;
}
}
结论:在同一个方法中A和B在同一个事务内部,B没加事务但是有默认事务,B方法出现异常,会导致A也回滚
- A方法有事务,B方法有事务
@Service
public class DaServiceImpl extends ServiceImpl<DaDao, Da> implements DaService {
@Transactional
@Override
public void insertEntity() {
Da da = new Da();
da.setCode("测试1");
da.setDrawingCode("图片地址1");
this.save(da);
updateEntity(da);
}
@Transactional
public void updateEntity(Da dd){
dd.setDrawingCode("修改地址");
this.updateById(dd);
int sum =1/0;
}
}
结论:A和B在同一个事务内部,B只是增加一个事务注解,但是没有指定事务的传播机制,同上也是有默认的,B出现异常,A也会回滚
- A方法有事务,B方法没有事务(B方法自己把异常捕捉了)
@Service
public class DaServiceImpl extends ServiceImpl<DaDao, Da> implements DaService {
@Transactional
@Override
public void insertEntity() {
Da da = new Da();
da.setCode("测试1");
da.setDrawingCode("图片地址1");
this.save(da);
updateEntity(da);
}
@Transactional
public void updateEntity(Da dd){
dd.setDrawingCode("修改地址");
this.updateById(dd);
try {
int sum =1/0;
} catch (Exception e) {
e.printStackTrace();
}
}
}
结论:这个时候是会报错:A的事务是不会回滚的,因为在A方法认为是没有接收到异常的,B本身抛了一个位置异常被自己吃了,解决方法要么就手动回滚,要么就抛出runtimeException 及他的子类
@Transactional
public void updateEntity(Da dd){
dd.setDrawingCode("修改地址");
this.updateById(dd);
try {
int sum =1/0;
} catch (Exception e) {
throw e;//抛出
}
}
@Transactional
public void updateEntity(Da dd){
dd.setDrawingCode("修改地址");
this.updateById(dd);
try {
int sum =1/0;
} catch (Exception e) {
//手动回滚
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
}
- A方法没有事务,B方法有事务,用A去调用B,B出现异常,会回滚吗?
public void A() {
B();
}
@Transactional
public void B(){
//dd.setDrawingCode("修改地址");
Da byId = this.getById("6881170a32c6b165f18b6f98137ca045");
byId.setCode("测试");
this.updateById(byId);
int sum =1/0;
}
结论:不会,因为事务的起始在A,A没有添加事务,也就是说,this.updateById(byId);这个代码是会执行到数据库的,且int sum =1/0;这句也会报错,就跟B没有任何事务一样
解决办法
1.使用代理(需要在主启动类上加上@EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true))开启aop和代理
public void A() {
DaServiceImpl proxy = (DaServiceImpl) AopContext.currentProxy();
proxy.B();
}
2.自己注入自己
@Autowired
DaServiceImpl daService;
public void A() {
daService.B();
}
这两种方法解决等同于A.B方法不在同一个类中,A(无事务)调用B(有事务)