14. Spring事物管理器

事物

  • 涉及到数据一致性问题,较为重要
  • 保证ACID

事物管理的目的

使得一整个业务 以原子性的方式执行 。解决如下问题 :

public void operate() {
    SqlSession sqlSession = getSqlSession() ;
    UserMapper userMapper = sqlSession.getMapper(UserMapper.xml) ; 
    User user = new User(6,"啦啦啦","2131123");
   	addUser(user);
    deleteUserById(6);
}

在上面的例子中 , 如果 deleteUserById(6)操作出错 ,addUser(user) 却正确执行了 。 数据库中添加了相应的用户信息但是结果却并不符合用户的预期

解决方案 开启mybatis-spring的事物管理功能

一个使用 MyBatis-Spring 的其中一个主要原因是它允许 MyBatis 参与到 Spring 的事务管理中。而不是给 MyBatis 创建一个新的专用事务管理器,MyBatis-Spring 借助了 Spring 中的 DataSourceTransactionManager 来实现事务管理。

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

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

标准配置

要开启 Spring 的事务处理功能,在 Spring 的配置文件中创建一个 DataSourceTransactionManager 对象:

<!--xml配置-->
<bean id="transactionManager"class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  <constructor-arg ref="dataSource" />
</bean>
//Java代码配置
@Configuration
public class DataSourceConfig {
  @Bean
  public DataSourceTransactionManager transactionManager() {
    return new DataSourceTransactionManager(dataSource());
  }
}

传入的 DataSource 可以是任何能够与 Spring 兼容的 JDBC DataSource。包括连接池和通过 JNDI 查找获得的 DataSource

注意:为事务管理器指定的 DataSource 必须和用来创建 SqlSessionFactoryBean 的是同一个数据源,否则事务管理器就无法工作了。

声明式事物

  1. 如果你正使用一个 JEE 容器而且想让 **Spring 参与到容器管理事务(Container managed transactions,CMT)**的过程中,那么 Spring 应该被设置为使用 JtaTransactionManager 或由容器指定的一个子类作为事务管理器。最简单的方式是使用 Spring 的事务命名空间或使用 JtaTransactionManagerFactoryBean
<!--xml配置方式-->
<tx:jta-transaction-manager />
//Java配置方式
@Configuration
public class DataSourceConfig {
  @Bean
  public JtaTransactionManager transactionManager() {
    return new JtaTransactionManagerFactoryBean().getObject();
  }
}

在这个配置中,MyBatis 将会和其它由容器管理事务配置的 Spring 事务资源一样。**Spring 会自动使用任何一个存 在的容器事务管理器,并注入一个 SqlSession。 **如果没有正在进行的事务,而基于事务配置需要一个新的事务的时 候,Spring 会开启一个新的由容器管理的事务。

2.注意,如果你想使用由容器管理的事务,而不想使用 Spring 的事务管理,你就不能配置任何的 Spring 事务管理器。并必须配置 SqlSessionFactoryBean 以使用基本的 MyBatis 的 ManagedTransactionFactory

<!--xml配置方式-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
  <property name="dataSource" ref="dataSource" />
  <property name="transactionFactory">
    <bean class="org.apache.ibatis.transaction.managed.ManagedTransactionFactory" />
  </property>
</bean>
//Java配置方式
@Configuration
public class MyBatisConfig {
  @Bean
  public SqlSessionFactory sqlSessionFactory() {
    SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
    factoryBean.setDataSource(dataSource());
    factoryBean.setTransactionFactory(new ManagedTransactionFactory());
    return factoryBean.getObject();
  }
}

3.结合AOP进行事务管理

<!--头文件注入-->
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/tx
        https://www.springframework.org/schema/tx/spring-tx.xsd">
<!--配置事务管理器 必要操作-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <constructor-arg ref="dataSource" />
</bean>
<!--结合AOP实现事物的注入-->
<!--配置 事物通知 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <!--给哪些方法配置事物-->
    <tx:attributes>
        <!--配置事物的传播特性 propagation-->
        <tx:method name="add*" propagation="REQUIRED"/> <!--给所有add开头方法以REQUIRED方式绑定事物-->
        <tx:method name="delete*" propagation="REQUIRED"/>
        <tx:method name="update*" propagation="REQUIRED"/>
        <tx:method name="query*" propagation="REQUIRED"/>
        <tx:method name="*" propagation="REQUIRED"/><!--给所有方法以REQUIRED方式绑定事物-->
    </tx:attributes>
</tx:advice>

<!--配置事物切入-->
<aop:config>
    <!--配置事物横切点-->
    <aop:pointcut id="txPointCut" expression="execution(* com.liu.mapper.*.*(..))"/>
    <!--配置事物增强-->
    <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"></aop:advisor>
</aop:config>
propagation 事物的传播特性
  1. PROPAGATION_REQUIRED: 支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择,也是 默认选择
  2. PROPAGATION_SUPPORTS: 支持当前事务,如果当前没有事务,就以非事务方式执行。
  3. PROPAGATION_MANDATORY: 支持当前事务,如果当前没有事务,就抛出异常。
  4. PROPAGATION_REQUIRES_NEW: 新建事务,如果当前存在事务,把当前事务挂起。
  5. PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
  6. PROPAGATION_NEVER: 以非事务方式执行,如果当前存在事务,则抛出异常。
  7. PROPAGATION_NESTED: 支持当前事务,如果当前事务存在,则执行一个嵌套事务,如果当前没有事务, 就新建一个事务。

编程式事务管理

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

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

无论 JDBC 连接是否设置为自动提交,调用 SqlSession 数据方法或在 Spring 事务之外调用任何在映射器中方法,事务都将会自动被提交。

如果你想编程式地控制事务,请参考 the Spring reference document(Data Access -Programmatic transaction management-) 。下面的代码展示了如何使用 PlatformTransactionManager 手工管理事务。

public class UserService {
  private final PlatformTransactionManager transactionManager;
  public UserService(PlatformTransactionManager transactionManager) {
    this.transactionManager = transactionManager;
  }
  public void createUser() {
    TransactionStatus txStatus =
        transactionManager.getTransaction(new DefaultTransactionDefinition());
    try {
      userMapper.insertUser(user);
    } catch (Exception e) {
      transactionManager.rollback(txStatus);
      throw e;
    }
    transactionManager.commit(txStatus);
  }
}

在使用 TransactionTemplate 的时候,可以省略对 commitrollback 方法的调用。

public class UserService {
  private final PlatformTransactionManager transactionManager;
  public UserService(PlatformTransactionManager transactionManager) {
    this.transactionManager = transactionManager;
  }
  public void createUser() {
    TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
    transactionTemplate.execute(txStatus -> {
      userMapper.insertUser(user);
      return null;
    });
  }
}

spring中的事物管理

  • 声明式事物 : AOP
  • 编程式事物 : 在代码中进行事物管理