Spring事务失效原因详解

Spring事务依赖的是数据库的事务,在开发中如果没有处理好,可能会遇到事务失效的情况,本文就盘点Spring事务会失效的一些情况并给出解决方案。
Spring事务分为声明式事务和编程时事务两种。看下声明式事务。

使用@Transactional做声明式事务的一些说明

  • @Transactional等价与@Transactional(propagation=Propagation.REQUIED),如果当前线程中存在事务,则使用该事务执行,如果不存在,就新创建一个事务。
  • @Transactional(propagation=Propagation.REQUIED_NEW),不管当前线程是否存在事务,都会新建一个新的事务继续执行,执行完毕之后,唤醒之前挂起的事务,继续执行。

使用@Transactional做声明式事务的流程

  • 创建事务(getTransaction)
  • 获取数据库连接
  • 提交事务(commit)
  • 回滚事务(rollback)
  • 释放数据库连接

通过几个实例来解释事务失效的问题

  • 情况1:
    要求:执行child()是开启一个新的事务。
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

/**
 * DESCRIPTION:
 *  * @author liu.qq
 * @create 2019-09-29 14:16
 **/
@Service
@Slf4j
public class TestService {
    @Autowired
    private OrderMapper orderMapper;

    @Transactional
    public void parent() {

        try {
            //child被this调用
            child();
        } catch (Exception e) {
            log.info("出现异常:");
        }

        Order order = new Order();
        order.setOrderNo("parent");
        order.setTitle("parent");
        order.setStatus(0);
        order.setAmount(100);
        orderMapper.insert(order);
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void child() {
        Order order = new Order();
        order.setOrderNo("parent");
        order.setTitle("parent");
        order.setStatus(0);
        order.setAmount(100);
        orderMapper.insert(order);
        int a = 1 / 0;
    }
}

执行结果:child()执行成功,parent()执行成功,child()事务失效的根本原因是动态代理导致的,只有被代理对象直接调用的方法才能被代理。这里是TestService 对象直接调用的,而不是其代理对象。所以child()方法的执行就相当于普通的方法的执行。

解决方案1:

从AOP上下文获取本类的代理对象,使用代理对象执行child()。

   @Transactional
    public void parent() {

        try {
//            child();
            TestService testService = (TestService)AopContext.currentProxy();
            testService.child();
        } catch (Exception e) {
            log.info("出现异常:");
        }

        Order order = new Order();
        order.setOrderNo("parent");
        order.setTitle("parent");
        order.setStatus(0);
        order.setAmount(100);
        orderMapper.insert(order);
    }

执行结果:child()失败,parent()成功。

解决方案2:

从Spring容器获取被代理类的代理对象,使用代理对象执行child()方法。

package cn.arbexpress.court.transactional;

import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

/**
 * DESCRIPTION:
 *
 * @author liu.qq
 * @create 2019-09-29 14:16
 **/
@Service
@Slf4j
public class TestService implements ApplicationContextAware {
    @Autowired
    private OrderMapper orderMapper;

    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    @Transactional
    public void parent() {

        try {
            TestService bean = applicationContext.getBean(TestService.class);
            bean.child();
        } catch (Exception e) {
            log.info("出现异常:");
        }

        Order order = new Order();
        order.setOrderNo("parent");
        order.setTitle("parent");
        order.setStatus(0);
        order.setAmount(100);
        orderMapper.insert(order);
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void child() {
        Order order = new Order();
        order.setOrderNo("parent");
        order.setTitle("parent");
        order.setStatus(0);
        order.setAmount(100);
        orderMapper.insert(order);
        int a = 1 / 0;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值