事务
- 涉及到数据的一致性问题
- 确保完整性和一致性
- ACID原则:
- 原子性:要么都成功,要么都失败;
- 一致性:事务前后数据的完整性没有被破坏;
- 隔离性:事务与事务之间相互隔离;
- 持久性:事务完成后,数据的修改是永久的,不会因为机器重启等原因丢失;
- 事务举例:
insertUser();//可以正确执行 deleteUser();//执行报错 //结果:insert成功,delete失败
普通情况下,delete未成功执行,但insert仍然会执行,我们如果想要这两个事务操作为一个整体,即如果delete失败,那么insert也不要执行。此时,我们就需要声明式事务;
- MyBatis-Spring 允许 MyBatis 参与到 Spring 的事务管理中。而不是给 MyBatis 创建一个新的专用事务管理器,MyBatis-Spring 借助了 Spring 中的 DataSourceTransactionManager 来实现事务管理。
- 一旦配置好了 Spring 的事务管理器,就支持 @Transactional 注解和 AOP 风格的事务配置。
- 在事务处理期间,一个单独的 SqlSession 对象将会被创建和使用。当事务完成时,这个 session 会以合适的方式提交或回滚。
- 事务配置好了以后,MyBatis-Spring 将会透明地管理事务。这样在你的 DAO 类中就不需要额外的代码了。
- 声明式事务:AOP
- 编程式事务:硬编码形式在代码中,进行事务管理
- 在spring配置文件中,配置事务管理器,并注入DataSource
注意:为事务管理器指定的 DataSource 必须和用来创建 SqlSessionFactory的是同一个数据源,否则事务管理器就无法工作了。
- 结合AOP实现事务的织入
- 配置事务通知的切面:引入tx约束,设置事务管理器、事务装配的方法、以及事务传播特性:
Spring事务七大传播特性:
REQUIRED: 支持当前事务,如果当前没有事务,就新建一个事务。【默认】
SUPPORTS: 支持当前事务,如果当前没有事务,就以非事务方式执行。
MANDATORY: 支持当前事务,如果当前没有事务,就抛出异常。
REQUERED_NEW: 新建事务,如果当前存在事务,就把当前事务挂起。
NOT_SUPPORTED: 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
NEVER: 以非事务方式执行操作,如果当前存在事务,就抛出异常。
NESTED: 支持当前事务,如果当前存在事务,就执行一个嵌套事务,如果当前没有食物,就新建一个事务; - 将事务切面织入切入点:配置切入点+切入进切入点
- 测试结果:delete执行失败后,insert也没执行;
- 配置事务通知的切面:引入tx约束,设置事务管理器、事务装配的方法、以及事务传播特性:
-
交由容器管理事务
如果你正使用一个 JEE 容器而且想让 Spring 参与到容器管理事务的过程中,那么 Spring 应该被设置为使用 JtaTransactionManager 或由容器指定的一个子类作为事务管理器。最简单的方式是使用 Spring 的事务命名空间或使用 JtaTransactionManagerFactoryBean:@Configuration public class DataSourceConfig { @Bean public JtaTransactionManager transactionManager() { return new JtaTransactionManagerFactoryBean().getObject(); } }在这个配置中,MyBatis 将会注入一个 SqlSession。 注意,如果你想使用由容器管理的事务,而不想使用 Spring 的事务管理,你就不能配置任何的 Spring 事务管理器。并必须配置 SqlSessionFactoryBean 以使用基本的 MyBatis 的 ManagedTransactionFactory:
@Configuration public class MyBatisConfig { @Bean public SqlSessionFactory sqlSessionFactory() { SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean(); factoryBean.setDataSource(dataSource()); factoryBean.setTransactionFactory(new ManagedTransactionFactory()); return factoryBean.getObject(); } } -
编程式事务管理
MyBatis 的 SqlSession 提供几个方法来在代码中处理事务。但是当使用 MyBatis-Spring 时,你的 bean 将会注入由 Spring 管理的 SqlSession 或映射器。也就是说,Spring 总是为你处理了事务。你不能在 Spring 管理的 SqlSession 上调用 SqlSession.commit(),SqlSession.rollback() 或 SqlSession.close() 方法。如果这样做了,就会抛出 UnsupportedOperationException 异常。在使用注入的映射器时,这些方法也不会暴露出来。
无论 JDBC 连接是否设置为自动提交,调用 SqlSession 数据方法或在 Spring 事务之外调用任何在映射器中方法,事务都将会自动被提交。
如果你想编程式地控制事务,使用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; }); } }注意:虽然这段代码使用的是一个映射器,但换成 SqlSession 也是可以工作的。



