为什么spring事务不生效?

开篇:总体战线拉的有些长,所以没有面面俱到,但有时间的话,后续文章会补上
环境介绍:win7,mysql5.6 ,mysql是新安装的,如果没有mysql的话,可以下载我上篇分享的链接,中间有段插曲,叙述如下,代码集成完毕,但是事物是真的不生效,思来想去,想到mysql的储存引擎是否是innodb,命令如下

show ENGINES; //显示支持的储存引擎,mysam不支持事物。
ALTER TABLE user ENGINE = InnoDB; 使用mysql innodb储存引擎。
重试单元测试,可以正常事物回滚了。

原理简述:spring事物是基于aop事物增强,aop底层实现俩种方式 一种是jdk,另一种是cglib
我们知道spring事物是基于动态代理实现,动态代理会有拦截器,对目标类进行处理,会在在目标方法开始执行之前创建并加入事务,并执行目标方法的逻辑, 最后根据执行情况是否出现异常,利用抽象事务管理器进行提交或者回滚,主要设计几个类如下
TransactionManager
在这里插入图片描述
TransactionDefinition,定义了事物隔离级别等
在这里插入图片描述
在这里插入图片描述
TransactionAttribute继承了TransactionDefinition
TransactionStatus
在这里插入图片描述

哪些方法能进行动态代理?不能代理意味着事物不生效
jdk:接口中的方法都是public ,所有只能是 public 或 public final,同时不能使用static
cglib:不能使用 final,static 和 private 修饰符 ,因为这些修饰符不能被子类覆盖,cglib基于扩展子类来实现代理

为什么Transactional 只能应用到 public 方法才有效 ?
源码执行流程,
事物拦截器生产代理 --》invoke–》invokeWithinTransaction–》getTransactionAttribute–》computeTransactionAttribute
在这里插入图片描述

正确的设置@Transactional 的 rollbackFor 属性,Transactional默认为运行时异常,如果不指定异常,那么非运行时异常不会回滚 测试方法3

如果传播级别设置不对,也会发生事物不生效。
在这里插入图片描述

下面给出测试案例:

package com.example.demo.service.impl;

import com.example.demo.entity.Customer;
import com.example.demo.entity.User;
import com.example.demo.resp.UserRepository;
import com.example.demo.service.CustomerService;
import com.example.demo.service.UserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;


@RunWith(SpringRunner.class)
@SpringBootTest
public class UserServiceImplTest {
    @Autowired
    private UserService userService;

    @Autowired
    private UserRepository userRepository;
    @Autowired
    CustomerService customerService;

    /**
     * 测试 事物是否正常,数据库表中的数据为空
     * 确实测试出问题了,再确定代码无误的情况下,请考虑数据库的存储引擎
     */
    @Test
    public void testSave() {
        User user = new User();
        user.setName("hhhh4");
        user.setEmail("nihao@163.com4");
        userService.save(user);
    }

    /**
     * 事物正常回滚
     * save中为运行时异常  int i=1/0
     */
    @Test
    public void testSave1() {
        User user = new User();
        user.setName("hhhh4");
        user.setEmail("nihao@163.com4");
        userService.save(user);
    }

    /**
     * 同一个类中save方法没有事物的方法,save2有事物的方法
     * 事物不会发生回滚,事物被忽略
     * <p>
     * 原因:只有目标方法由外部调用,目标方法才由 Spring 生成的代理对象来管理
     */
    @Test
    public void testSave5() {
        User user = new User();
        user.setName("hhhh5");
        user.setEmail("nihao@163.com5");
        userService.save(user);
    }

    /**
     * 同一个类中俩个方法都有事物
     * 会发生回滚
     */
    @Test
    public void testSave6() {
        User user = new User();
        user.setName("hhhh6");
        user.setEmail("nihao@163.com6");
        userService.save(user);
    }


    /**
     * 同一个类中 save 方法有事物,save2 没有事物
     * 会发生回滚
     */
    @Test
    public void testSave7() {
        User user = new User();
        user.setName("hhhh7");
        user.setEmail("nihao@163.com7");
        userService.save(user);
    }

    /**
     * 测试方法3
     */
    @Test
    public void testSave8() throws IllegalAccessException {
        User user = new User();
        user.setName("hhhh8");
        user.setEmail("nihao@163.com8");
        userService.save3(user);
    }


    @Test
    public void testSave9() throws IllegalAccessException {
        User user = new User();
        user.setName("hhhh8");
        user.setEmail("nihao@163.com8");
        userService.save3(user);
    }


    /**
     * 重点  应该由外部直接调用俩个方法,避免方法2 没有被增强
     * 事物2 方法2中有事物注解,但是没有被增强,所以事物并没有被回滚
     */
    @Test
    public void testSave10() {
        customerService.save();
    }
}

参考文章:https://www.ibm.com/developerworks/cn/java/j-master-spring-transactional-use/index.html
参考书籍:精通企业spring5.x
工程地址如下:https://github.com/ylha/spring-tx.git,看着很简单吧? 自己试试 哈哈。总结的话都是测试中说了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值