Spring-TX
Spring-TX
编程式事务
参考 JDBC -> JDBC-事务
编程式事务是指手动编写程序来管理事务,即通过代码的方式直接控制事务的提交和回滚
在之前的学习中,我们使用JDBC中的连接来控制事务的提交和回滚
但是编程式实现方式存在缺陷:
- 细节没有被屏蔽:具体操作过程各种,所有细节都需要程序员来自己完成,比较繁琐
- 代码复用性低
声明式事务
声明式事务是指用注解或XML的方式来控制事务的提交和回滚
我们可以使用AOP的思路来写事务增强,但Spring对事务的AOP进行了再次的封装,更加简化了我们的操作
Spring事务管理器
在Spring的底层实现中,创建了一个事务增强,又制作了一个事务管理器统一规范的接口
通过事务增强去调用事务管理器的接口完成事务的具体操作(开启,提交,回滚)
又针对于不同的框架制作了不同的事务管理器的具体实现类
当我们选择某一个具体框架的事务管理器实现类时,Spring会将这个实现类自动注入到事务增强调用的事务管理器的接口
- TransactionManager接口:事务管理器的统一接口
- PlatformTansactionManager:统一接口的子接口,扩展实现
- DataSourceTransactionManager:实现类
- JDBC方式
- JdbcTemplate方式
- Mybatis方式
- HibernateTransactionManager:实现类
- hibernate方式
- JpaTransactionManager
- jpa方式
- DataSourceTransactionManager:实现类
注:DataSourceTransactionManager由spring-jdbc依赖提供,HibernateTransactionManager和JpaTransactionManager由spring-orm依赖提供
DataSourceTransactionManager
我们学习的事务管理器实现是DataSourceTransactionManager,用以整合JDBC、jdbcTemplate、Mybatis方式的事务实现
DataSourceTransactionManager的主要方法:
- doBegin():开启事务
- doSuspend():挂起事务
- doResume():恢复挂起的事务
- doCommit():提交事务
- doRollback():回滚事务
然而以上的方法我们都不会自己使用,因为框架帮我们完成了一切,我们只需要使用注解声明即可
基于注解的声明式事务
导入依赖:
- spring-jdbc
- spring-tx
从6.1.8版本开始,tx为jdbc自动导入的依赖,所以无需导入
首先,我们在配置类中将事务管理器的实现添加到IoC容器中
事务管理器的实现中,要进行对事务的操作,需要使用连接池对象
@Bean
public TransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
@EnableTransactionManagement
在配置类中加@EnableTransactionManagement注解,开启对事务注解的支持
@Transactional
这个注解的作用就是开启事务,它可以加载方法和类上:
- 方法:方法开启事务
- 类:类中所有方法开启事务
此时,方法就被Spring的事务管理器以AOP的方式接管了,如果方法执行有异常,则整个方法回滚,如果方法执行顺利,则事务提交
readOnly
只读模式可以提高查询事务的效率,如果事务中只有查询代码,使用只读模式
@Transactional(readOnly = true)
当@Transactional注解到类时,推荐将类中调用查询的方法加入此注解和属性
timeout
事务在执行过程中,有可能因为遇到某些问题,导致程序卡住,从而长时间占用数据库
使用timeout属性设置事务的超时时间
@Transactional(timeout = 5) // 单位:秒
如果事务超时,则会报异常:TransactionTimedOutException
注:当类的注解有timeout属性时,方法如果也有注解但没有属性,方法的注解会覆盖类的注解!!!
rollbackFor&norollbackFor
事务的默认是发生运行时异常才会回滚,其他异常不会回滚(比如IO异常)
使用rollbackFor属性,调整为无论发生什么异常时都回滚事务
@Transactional(rollbackFor = Exception.class)
norollbackFor可以放行某些异常,使某些异常发生时不回滚事务
事务隔离级别
数据库事务的隔离级别是指在多个事务并发执行时,数据库系统为了保证数据一致性所遵循的规定。常见的隔离级别包括:
-
读未提交(Read Uncommitted):事务可以读取未被提交的数据,容易产生脏读、不可重复读和幻读等问题。实现简单但不太安全,一般不用。
-
读已提交(Read Committed):事务只能读取已经提交的数据,可以避免脏读问题,但可能引发不可重复读和幻读。
-
可重复读(Repeatable Read):在一个事务中,相同的查询将返回相同的结果集,不管其他事务对数据做了什么修改。可以避免脏读和不可重复读,但仍有幻读的问题。
-
串行化(Serializable):最高的隔离级别,完全禁止了并发,只允许一个事务执行完毕之后才能执行另一个事务。可以避免以上所有问题,但效率较低,不适用于高并发场景。
不同的隔离级别适用于不同的场景,需要根据实际业务需求进行选择和调整
MySQL的默认隔离级别是可重复读(Repeatable Read)
使用isolation属性设置隔离级别,推荐设置为读已提交(Read Committed)
@Transactional(isolation = Isolation.READ_COMMITTED)
事务传播行为
事务之间也存在互相调用的情况,以事务的传播行为来决定事物之间调用的影响程度
事务传播行为的属性设定,是设置在子事务(方法)上的
使用propagation 属性设置事务的传播行为
@Transactional(propagation = Propagation.REQUIRES_NEW)
名称 | 含义 |
---|---|
REQUIRED 默认值 | 如果父方法有事务,就加入,如果没有就新建自己独立! |
REQUIRES_NEW | 不管父方法是否有事务,我都新建事务,都是独立的! |
如果父方法调用子方法,父方法有事务,使用REQUIRED时,子方法会加入父方法的事务中,形成一个事务
如果使用REQUIRES_NEW属性,方法之间事务独立,相互不影响
事务的传播行为一般都推荐用默认值,只有面试的情况下才会问相关问题!!!
在同一个类中,事务的传播行为不会生效,这是因为Spring框架中实际上是使用了代理模式实现事务机制
而代理是根据类来生成增强类,同一个类间的调用就不经过代理