MyBatis 入门系列【15】 Spring 事务管理器

25 阅读2分钟

1. 概述

在使用Spring或者Spring Boot集成MyBatis时,允许MyBatis 参与到 Spring 的事务管理中,而不是给 MyBatis 创建一个新的专用事务管理器,借助了 Spring 中的 DataSourceTransactionManager 来实现事务管理。

一旦配置好了 Spring 的事务管理器,就可以在 Spring 中按你平时的方式来配置事务。并且支持 @Transactional 注解和 AOP 风格的配置。在事务处理期间,一个单独的 SqlSession 对象将会被创建和使用。当事务完成时,这个 session 会以合适的方式提交或回滚。

事务配置好了以后,Spring 将会透明地管理事务,这样在你的 DAO 类中就不需要额外的代码了。

2. 编程式事务管理

编程式事务:通过编程代码在业务逻辑时需要时自行实现,粒度更小。

MyBatisSqlSession 提供几个方法来在代码中处理事务。但是当集成了Spring 时,你的 bean 将会注入由 Sprin 管理的 SqlSession 或映射器。也就是说,Spring 总是为你处理了事务。

不能在 Spring 管理的 SqlSession 上调用 SqlSession.commit()SqlSession.rollback()SqlSession.close() 方法。这样做就会抛出 UnsupportedOperationException 异常。在使用注入的映射器时,这些方法也不会暴露出来。

Spring Framework 提供了两种编程事务管理的方法,通过使用:

  • TransactionTemplateTransactionalOperator

  • TransactionManager实现类。

首先我们注入PlatformTransactionManager

    /**
     * PlatformTransactionManager 编程式事务管理器
     */
    @Bean
    public PlatformTransactionManager platformTransactionManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

然后在执行SQL的代码中加入手动事务管理:

    @Autowired
    PlatformTransactionManager transactionManager;

    public void test(String address) {
        TransactionStatus txStatus = transactionManager.getTransaction(new DefaultTransactionDefinition());
        try {
           userMapper.update(address);
            int i = 5 / 0;
        } catch (Exception e) {
            // 发生异常回滚事务
            transactionManager.rollback(txStatus);
            throw e;
        }
        // 没有异常,提交事务
        transactionManager.commit(txStatus);
    }

执行操作,发生异常,发现数据没有变化,事务回滚成功。

3. 声明式事务管理

大多数 Spring Framework 用户选择声明式事务管理。此选项对应用程序代码的影响最小,因此最符合非侵入式轻量级容器的理想。

只需要添加@EnableTransactionManagement开启事务管理,然后在方法上添加@Transactional注解,就可以开启声明式事务管理。

首先在配置类加上@EnableTransactionManagement注解,并声明DataSourceTransactionManagerBean对象。

@Configuration
@MapperScan(basePackages = {"org.pearl.spring.mybatis.demo.dao"})
@EnableTransactionManagement
public class MyBatisConfig {

    /**
     * 注入SqlSessionFactoryBean
     */
    @Bean
    public SqlSessionFactory sqlSessionFactory() throws Exception {
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        factoryBean.setDataSource(dataSource());
        return factoryBean.getObject();
    }

    /**
     * 设置Druid数据源,配置相关属性
     */
    @Bean
    public DataSource dataSource() {
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setUrl("jdbc:mysql://127.0.0.1:3306/angel_admin?serverTimezone=Asia/Shanghai");
        druidDataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        druidDataSource.setUsername("root");
        druidDataSource.setPassword("123456");
        return druidDataSource;
    }
    @Bean
    public DataSourceTransactionManager transactionManager() {
        return new DataSourceTransactionManager(dataSource());
    }
}

然后直接在业务方法上加上 @Transactional注解。

    @Transactional
    public void test(String address) {
        userMapper.update(address);
        int i = 5 / 0;
    }

执行操作,发生异常,发现数据没有变化,事务回滚成功。可以看到报错信息,有AOP和拦截器相关的类,说明@Transactional注解用到了AOP代理。

image.png