这篇文章主要介绍了Spring事务失效问题分析及解决方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
隔离级别
在 TransactionDefinition.java 接口中,定义了“四种”的隔离级别枚举:
int ISOLATION_DEFAULT = -1; int ISOLATION_READ_UNCOMMITTED = Connection.TRANSACTION_READ_UNCOMMITTED; int ISOLATION_READ_COMMITTED = Connection.TRANSACTION_READ_COMMITTED; int ISOLATION_REPEATABLE_READ = Connection.TRANSACTION_REPEATABLE_READ; int ISOLATION_SERIALIZABLE = Connection.TRANSACTION_SERIALIZABLE;
事务的传播级别
事务的传播行为,指的是当前带有事务配置的方法,需要怎么处理事务;例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行;
需要注意,事务的传播级别,并不是数据库事务规范中的名词,而是 Spring 自身所定义的。通过事务的传播级别,Spring 才知道如何处理事务,是创建一个新事务呢,还是继续使用当前的事务;
在 TransactionDefinition.java 接口中,定义了三类七种传播级别:
// ========== 支持当前事务的情况 ========== int PROPAGATION_REQUIRED = 0; int PROPAGATION_SUPPORTS = 1; int PROPAGATION_MANDATORY = 2; // ========== 不支持当前事务的情况 ========== int PROPAGATION_REQUIRES_NEW = 3; int PROPAGATION_NOT_SUPPORTED = 4; int PROPAGATION_NEVER = 5; // ========== 其他情况 ========== int PROPAGATION_NESTED = 6;
@Transaction
@Transaction 注解是Spring的tx模块提供的,使用AOP实现的事务控制,即底层为动态代理;
@Transaction 可以作用于接口、接口方法、类以及类方法上;当作用于类上时,该类的所有 public 方法将都具有该类型的事务属性,也可以在方法级别使用该标注来覆盖类级别的定义;
事务失效
1.异常类型错误,默认是RuntimException才会回滚
2.异常被catch后没有抛出,需要抛异常才能回滚
3.是否发生自身调用的问题
4.注解所在位置是否为public修饰
5.数据源没有配置事务管理器
6.不支持事务的引擎,如MyIsam
下面是一个事务失效的例子
@Slf4j
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private OrderMapper orderMapper;
@Transactional
@Override
public void parent() {
try {
this.child();
} catch (Exception e) {
log.error("插入异常", e);
}
Order order = new Order();
order.setOrderNo("parent");
order.setStatus("0");
order.setTitle("parent");
order.setAmount("1000");
orderMapper.insert(order);
}
@Transactional
@Override
public void child() {
Order order = new Order();
order.setOrderNo("child");
order.setStatus("0");
order.setTitle("child");
order.setAmount("2000");
orderMapper.insert(order);
throw new RuntimeException();
}
}
当调用parent方法时,会调用child方法,但执行完成后插入的记录有两条,一条是parent的,一条是child的;
这是因为上面的parent方法调用的child方法出现问题,@Transaction 是基于AOP的方式进行事务控制的(CglibAopProxy.java进行方法拦截,TransactionInterceptor.java进行代理对象调用执行方法),需要使用的是代理对象调用方法,上面的代码使用的还是this,即当前实例化对象,因此执行this.child(),方法不能被拦截增强;
将上面的parent方法修改如下
@Autowired
private ApplicationContext context;
private OrderService orderService;
@PostConstruct
public void init() {
orderService = context.getBean(OrderService.class);
}
@Transactional
@Override
public void parent() {
try {
//获取代理对象,通过代理对象调用child()
orderService.child();
//获取代理对象
//OrderService orderService = (OrderService) AopContext.currentProxy();
//orderService.child();
} catch (Exception e) {
log.error("插入异常", e);
}
Order order = new Order();
order.setOrderNo("parent");
order.setStatus("0");
order.setTitle("parent");
order.setAmount("1000");
orderMapper.insert(order);
}
执行parent方法,当出现异常的时候,事务会进行回滚;
如果想实现当调用parent方法时,调用child方法发生异常,只回滚child方法插入的数据,parent方法插入的数据不回滚,修改如下
@Transactional(propagation = Propagation.REQUIRES_NEW)
@Override
public void child() {
Order order = new Order();
order.setOrderNo("child");
order.setStatus("0");
order.setTitle("child");
order.setAmount("2000");
orderMapper.insert(order);
throw new RuntimeException();
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
如果当前存在事务,则挂起事务并开启一个新事务执行,新事务执行完毕后,唤醒之前的挂起的事务,则继续执行;如果当前不存在事务,则新建一个事务;
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持考高分网。



