同一个类的不同方法之间的调用(以方法 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 ApplicationContext applicationContext;
@Override
public void methodA() {
// ...业务操作
ServiceA serviceA = applicationContext.getBean(ServiceA.class);
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 失效的场景还有:
评论