@Transactional注解为什么不生效

一、背景

  • 方法添加了@Transactional注解,为什么事务不生效

二、步骤

  • 测试用表结构
CREATE TABLE `test_aop` (
    `id` int(11) NOT NULL AUTO_INCREMENT,
    `name` varchar(100) DEFAULT NULL,
    PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8
  • 事务方法所在类
@Component
@Slf4j
public class AopDataService {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Autowired
    private AopDataService aopDataService;

    // 写入数据
    @Transactional
    public void insert(String value) {
        jdbcTemplate.update("insert into test_aop(name) values('" + value + "')");
    }

    // 写入数据后抛异常
    @Transactional
    public void insertAndRollback(String value) {
        log.info("insertAndRollback {}", value);
        jdbcTemplate.update("insert into test_aop(name) values('" + value + "')");
        throw new RuntimeException();
    }

    // 使用this调用事务方法
    public void invokeMethod() {

        try {

            this.insertAndRollback("invokeMethod");

            // 这里的this就是AopDataService,不是代理对象。
            // 所以调用的insertAndRollback方式是原生方法,不是@Transactional注解代理后的对象

        } catch (RuntimeException ex) {
            log.warn("Catch an Exception in invokeMethod()");
        }
    }

    // 使用this.aopDataService调用事务方法
    public void invokeMethodAutowired() {

        try {

            this.aopDataService.insertAndRollback("invokeMethodAutowired");
            // this.aopDataService 代理对象,事务生效

        } catch (RuntimeException ex) {
            log.error("Catch an Exception in invokeMethodAutowired()");
        }
    }

    // 使用getBean调用事务方法
    public void invokeMethodGetBean() {

        try {

            AopDataService aopDataService = SpringUtil.getBean("aopDataService", AopDataService.class);
            aopDataService.insertAndRollback("invokeMethodGetBean");

            // aopDataService 代理对象,事务生效

        } catch (RuntimeException ex) {
            log.warn("Catch an Exception in invokeMethodGetBean()");
        }
    }

}
  • 调用事务方法单元测试
  @Autowired
    private AopDataService aopDataService;

    // 写入成功(this.aopDataService是代理对象)
    @Test
    public void insert() {

        this.aopDataService.insert("insert");
    }

    // 抛异常,数据写入不成功(this.aopDataService是代理对象)
    @Test
    public void insertAndRollback() {

        this.aopDataService.insertAndRollback("insertAndRollback");
    }

    // 抛异常,数据写入成功(invokeMethod方法内未使用AopDataService代理对象)
    @Test
    public void invokeMethod() {

        this.aopDataService.invokeMethod();
    }

    // 抛异常,数据写入不成功(invokeMethodAutowired方法内使用了AopDataService代理对象)
    @Test
    public void invokeMethodAutowired() {

        this.aopDataService.invokeMethodAutowired();
    }

    // 抛异常,数据写入不成功(invokeMethodGetBean方法内使用了AopDataService代理对象)
    @Test
    public void invokeMethodGetBean() {

        this.aopDataService.invokeMethodGetBean();
    }

 

三、总结&问题

  • invokeMethod通过 this方式调用insertAndRollback方法,该方法抛异常但是写入成功,原因是
  这里的this就是 AopDataService,不是代理对象。 
  所以调用的insertAndRollback方式是原生方法,不是@Transactional注解代理后的对象
  • invokeMethodAutowired、invokeMethodGetBean调用成功是因为都使用的AopDataService代理对象
上一篇:记一次org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it h


下一篇:事务(@Transactional注解)的用法和实例