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
的是同一个数据源,否则事务管理器就无法工作了。
声明式事物
- 如果你正使用一个 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 事物的传播特性
- PROPAGATION_REQUIRED: 支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择,也是 默认选择
- PROPAGATION_SUPPORTS: 支持当前事务,如果当前没有事务,就以非事务方式执行。
- PROPAGATION_MANDATORY: 支持当前事务,如果当前没有事务,就抛出异常。
- PROPAGATION_REQUIRES_NEW: 新建事务,如果当前存在事务,把当前事务挂起。
- PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
- PROPAGATION_NEVER: 以非事务方式执行,如果当前存在事务,则抛出异常。
- 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
的时候,可以省略对 commit
和 rollback
方法的调用。
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
- 编程式事物 : 在代码中进行事物管理