同一个类的不同方法之间的调用(以方法A调用方法B为例)
如果方法A开启事务,则事务都可以生效
因为此时方法A走的是代理对象,所以事务会生效。并且方法B的默认传播机制是REQUIRED,即方法B会加入到方法A的事务中,他们便处于同一个事务当中,双方都会进行回滚。
@Transactional(rollbackFor = Exception.class)
@Override
public void methodA() {
// ...业务操作
methodB();
// 当A异常时,双方都会进行回滚。
int i = 10 / 0;
}
// 即使B没有事务,也会进行回滚。
@Transactional(rollbackFor = Exception.class)
@Override
public void methodB() {
// ...业务操作
// 同理,B异常时,A也会回滚,因为处于同一个事务中
// int i = 10 / 0;
}
如果方法A没有事务,则方法B无论怎样,事务都会失效
主要原因就是Spring的声明式事务是通过AOP代理实现的,所以必须由代理类调用对应的方法才能进行增强生效。由于方法A没有事务,则不会被代理,则其调用方法B时就是通过this自身来调用本类的方法,而不是代理类来调用。
@Override
public void methodA() {
// ...业务操作
methodB();
// 当A异常时,没有开启事务,不是走代理类,所以A和B都不会回滚。
int i = 10 / 0;
}
@Transactional(rollbackFor = Exception.class)
@Override
public void methodB() {
// ...业务操作
// 同理,B异常时,因为本身不是代理类进行调用,所以也不会回滚。
// int i = 10 / 0;
}
如何解决上述的问题呢
方法一:既然是同类操作引起的,则将方法A和方法B进行抽离即可解决,这样就能让代理类去进行调用。
@Service
public class ServiceA {
@Resource
private ServiceB serviceB;
@Override
public void methodA() {
// ...业务操作
serviceB.methodB();
}
}
@Service
public class ServiceB {
@Transactional(rollbackFor = Exception.class)
@Override
public void methodB() {
// ...业务操作
// 当这里发生异常时,B会进行回滚。
int i = 10 / 0;
}
}
方法二:不要让他进行this调用,通过代理类调用即可。
@Service
public class ServiceA {
@Resource
private ServiceA serviceA;
@Override
public void methodA() {
// ...业务操作
serviceA.methodB();
}
@Transactional(rollbackFor = Exception.class)
@Override
public void methodB() {
// ...业务操作
// 当B发生异常时,因为通过代理类进行调用,所以自身会进行回滚。
int i = 10 / 0;
}
}
方法三:也是跟方法二类似,不过是直接从IOC容器获取bean,这样也可以走代理。
@Service
public class ServiceA {
@Resource
private ServiceA serviceA;
@Resource
private ApplicationContext applicationContext;
@Override
public void methodA() {
// ...业务操作
serviceA.methodB();
}
@Transactional(rollbackFor = Exception.class)
@Override
public void methodB() {
// ...业务操作
// 当B发生异常时,因为通过代理类进行调用,所以自身会进行回滚。
int i = 10 / 0;
}
}
不同类的不同方法之间的调用(以方法A调用方法B为例)
如果方法A开启事务,则事务都可以生效
因为方法A和方法B都会被代理,同时根据默认的传播机制,此时方法A和B处于同一个事务中,只要A或者B回滚,他们都会进行回滚。
@Service
public class ServiceA {
@Resource
private ServiceB serviceB;
@Transactional(rollbackFor = Exception.class)
@Override
public void methodA() {
// ...业务操作
serviceB.methodB();
// 当这里发生异常时,A和B都会回滚。
int i = 10 / 0;
}
}
@Service
public class ServiceB {
// 即使B没有事务,也会进行回滚。
@Transactional(rollbackFor = Exception.class)
@Override
public void methodB() {
// ...业务操作
// 同理,当只有B异常时,A和B都会回滚。因为处于同一个事务中。
//int i = 10 / 0;
}
}
如果方法A没有事务,则方法B只有自身发生异常时才会回滚
@Service
public class ServiceA {
@Resource
private ServiceB serviceB;
@Override
public void methodA() {
// ...业务操作
serviceB.methodB();
// 当这里发生异常时,A和B都不会回滚。
int i = 10 / 0;
}
}
@Service
public class ServiceB {
@Transactional(rollbackFor = Exception.class)
@Override
public void methodB() {
// ...业务操作
// 只有B自己发生异常时,才会自行回滚。
//int i = 10 / 0;
}
}
补充说明
以上仅在单体架构中有效,实际的微服务场景,涉及到跨服务间调用的时候,就没办法生效,此时必须引入分布式事务来解决。另外其他可能导致@Transaction失效的场景还有:
- 方法是非public或者final的,事务会失效。
- 传播机制设置成非事务运行时也会失效。
- 数据库引擎不支持事务。
- 异常被捕获时,事务也会失效。
- 未配置事务管理器或者没有开启事务管理。
- 没有设置rollbackFor=Exception.class,默认只会处理RuntimeException。