【Spring声明式事务失效的12种场景测试】

文章目录

一.Spring声明式事务是什么?

Spring声明式事务是一种通过配置的方式管理事务的方法,它通过注解或XML配置来声明哪些方法需要事务管理,从而将事务管理逻辑与业务逻辑分离,简化了代码的复杂度,提高了代码的可读性和可维护性。‌

声明式事务的核心在于通过AOP(面向切面编程)技术实现事务管理的自动化。开发者只需在需要事务管理的方法上添加@Transactional注解,Spring框架会自动在方法执行前后进行事务的开启和关闭,以及在出现异常时进行事务的回滚。这种方式使得开发者可以专注于业务逻辑的实现,而不需要编写繁琐的事务管理代码‌。

具体实现方式上,声明式事务可以通过注解或XML配置来实现。使用注解方式时,只需在方法上添加@Transactional注解,Spring会自动处理事务的开启、提交和回滚。使用XML配置方式时,需要在Spring配置文件中进行相应的配置,指定哪些方法需要事务管理。这两种方式都使得事务管理的细节被屏蔽,开发者无需关心事务的具体实现细节‌

二.Spring事务失效的12种场景

我们在前面文章中了解过Spring编程式事务,看过Spring声明式事务源码的小伙伴们应该也清楚Spring声明式事务底层是借助于AOP+声明式事务去做的。我们知道Mybatis是基于数据源的方式,其编程式事务依赖于PlatformTransactionManager接口的实现DataSourceTransactionManager事务管理器进行事务管理(开启、提交、回滚)的。

所以我们使用Spring声明式事务需要往Spring容器中注入事务管理器所需的相关bean信息。

java 复制代码
//在Spring开启声明式事务支持时,启动类需要加@EnableTransactionManagement注解。‌ 
//这个注解告诉Spring容器要启用基于注解的事务管理功能,否则事务不生效
@EnableTransactionManagement
@ComponentScan({"com.jinbiao.spring_study.transactionTest"})
@Configuration
public class JDBCConfig {
    @Bean
    public DataSource dataSource(){
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setDriverClassName("com.mysql.jdbc.Driver");
        druidDataSource.setUrl("jdbc:mysql://localhost:3306/study_test");
        druidDataSource.setUsername("root");
        druidDataSource.setPassword("123456");
        return druidDataSource;
    }

    /**
     * 如果使用到mybatis需要给sqlSessionFactoryBean注入数据源信息
     * @param dataSource
     * @return
     */
    @Bean
    public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        return sqlSessionFactoryBean;
    }

    @Bean
    public JdbcTemplate jdbcTemplate(){
       return new JdbcTemplate(dataSource());
    }

    @Bean
    public PlatformTransactionManager transactionManager(){
        DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
        transactionManager.setDataSource(dataSource());
        return transactionManager;
    }
}

1.访问权限问题

事务方法使用 final 、static 修饰方法。

static 静态方法属于类本身的而非实例,代理对象是无法对静态方法进行代理或拦截的。

小伙伴们仔细想想,静态方法是在构造方法之前执行的,对象是通过构造方法创建的,jdk动态代理或者cglb动态代理都是针对目标对象增强生成代理对象。这样一想静态方法怎么被代理对象增强对吧!(说人话:静态方法被调用的时候,对象都还没创建,代理对象都还没出生呢,所以事务怎么生效对吧~)

  • 测试事务方法使用static 修饰,无法回滚问题。
  1. 错误的测试方式:
java 复制代码
   @Autowired
    private static JdbcTemplate jdbcTemplate;

    /**
     * 测试事务失效1:访问权限问题:事务方法使用 static修饰方法
     */
    @Transactional
    public static void test1() {
        /**
         * 静态方法里面直接从Spring容器里面取jdbcTemplate此时会为null,会报空指针
         * 需要用在bean初始化前方法里面填充好的userServiceTransaction对象的jdbcTemplate。
         */
        jdbcTemplate.execute("insert into jinbiao_user values (1,'rise1','wang1234..','10086','小程序')");
        throw new RuntimeException("异常啦,请回滚...");
    }

静态方法只能调用静态属性(不然程序编译期报错),我们注入的时候,把Spring容器里面的这个jdbcTemplate 使用static修饰升级为类变量,会报空指针,原理上面已解释:静态方法被调用的时候,对象都还没创建当然报空指针。

  1. 正确的测试方式:

  2. 在Spring生命周期中,@PostConstruct注解的方法是在属性填充之后执行的,此时的this是填充好属性jdbcTemplate的。

  3. 所以我们在bean初始化前方法里面用填充好属性的this对象赋值给静态成员变量userServiceTransaction

  4. 通过静态成员变量userServiceTransaction的属性jdbcTemplate来执行sql。

java 复制代码
 	@Autowired
    public static UserServiceTransaction userServiceTransaction;
    
    @Autowired
    private JdbcTemplate jdbcTemplate;

    @PostConstruct
    public void init(){
        userServiceTransaction = this;
    }

    /**
     * 测试事务失效1:访问权限问题:事务方法使用 static修饰方法
     */
    @Transactional
    public static void test1() {
        /**
         * 静态方法里面直接从Spring容器里面取jdbcTemplate此时会为null,会报空指针
         * 需要用在bean初始化前方法里面填充好的userServiceTransaction对象的jdbcTemplate。
         */
        userServiceTransaction.jdbcTemplate.execute("insert into jinbiao_user values (1,'rise1','wang1234..','10086','小程序')");
        throw new RuntimeException("异常啦,请回滚...");
    }

测试结果:数据没回滚。

  • 测试事务方法使用final 修饰,无法回滚问题。
    final 修饰的方法不能被子类重写,事务相关的逻辑无法插入到 final 方法中,代理机制无法对 final 方法进行拦截或增强。

下班了,未完持续....

小结

相关推荐
reasonsummer几秒前
【办公类-115-05】20250920职称资料上传04——PDF和PDF合并PDF、图片和PDF合并PDF(十三五PDF+十四五图片)
java·python·pdf
Mcband2 分钟前
Apache Commons IO:文件流处理利器,让Java IO操作更简单
java·开发语言·apache
缺点内向2 分钟前
Java:将 Word 文档转换为密码保护的 PDF 文件
java·pdf·word
骑士雄师38 分钟前
Java 泛型中级面试题及答案
java·开发语言·面试
曾令胜6 小时前
excel导出使用arthas动态追踪方法调用耗时后性能优化的过程
spring·性能优化·excel
.格子衫.6 小时前
Spring Boot 原理篇
java·spring boot·后端
多云几多7 小时前
Yudao单体项目 springboot Admin安全验证开启
java·spring boot·spring·springbootadmin
摇滚侠8 小时前
Spring Boot 3零基础教程,Spring Intializer,笔记05
spring boot·笔记·spring
Jabes.yang9 小时前
Java求职面试实战:从Spring Boot到微服务架构的技术探讨
java·数据库·spring boot·微服务·面试·消息队列·互联网大厂
聪明的笨猪猪9 小时前
Java Redis “高可用 — 主从复制”面试清单(含超通俗生活案例与深度理解)
java·经验分享·笔记·面试