Spring对事务的支持
Spring对事务的支持
首先我们要知道事务的实现是由数据库实现的,spring只是让我们更好的使用事务,如果你的数据库不支持事务,即使你在spring中使用了事务也不起作用。
一、spring的事务介绍
spring框架中对事务的支持有两种
- 编程式事务管理
- 声明式事务管理(推荐)
1.1 编程式事务
编程式事务管理:事务的相关操作完全由开发人员通过编码实现。所以编程式事务管理是侵入性事务管理,使用TransactionTemplate或者直接使用PlatformTransactionManager,对于编程式事务管理,Spring推荐使用TransactionTemplate。但是我们基本不推荐使用编程式事务。
下图展示的是使用原生jdbc对编程式事务的实现,完全有程序员来实现。
1 | try { |
至于TransactionTemplate、PlatformTransactionManager 两个类的作用我们后续讲解。
1.2 申明式事务
声明式事务管理:事务的控制交给Spring框架来管理,开发人员只需要在Spring框架的配置文件中声明你需要的功能即可。
Spring中声明式事务管理的底层是基于AOP来完成的,其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,执行完目标方法之后根据执行的情况提交或者回滚。声明式事务它将具体业务与事务处理部分解耦,代码侵入性很低,所以在实际开发中声明式事务用的比较多。
1 | try{ |
二、spring事务的相关接口
Spring事务管理的相关接口有四个,如下:
- PlatformTransactionManager:事务管理器,为不同的数据访问技术的事务提供不同的接口实现
- TransactionDefinition: 事务定义信息(事务隔离级别、传播行为、超时、只读、回滚规则)
- TransactionStatus: 事务的运行状态
- TransactionAttribute: 事务属性,实现了对回滚规则的扩展
Spring的事务机制是用统一的机制来处理不同数据访问技术的事务处理,Spring并不直接管理事务,而是提供了多种事务管理器。Spring的事务机制提供了一个org.springframework.transaction.PlatformTransactionManager接口,将事务管理的职责委托给JDBC或者Hibernate等持久化机制所提供的相关平台框架的事务来实现。通过这个接口,Spring为各个平台如JDBC、Hibernate等都提供了对应的事务管理器,其具体的实现就是各个平台自己的事情了,对应的相关实现如下表所示。
数据库访问技术 | 实现 |
---|---|
JDBC | DataSourceTransactionManager |
JPA | JpaTransactionManager |
Hibernate | HibernateJpaTransactionManager |
JDO | JdoTransactionManager |
分布式事务 | JtaTransactionManager |
三、基于注解的声明式事务
在Spring中使用声明式事务一般会使用注解来实现,即@Transactional注解,该注解可以使用在类、接口和方法上:
- 作用在类:表示所有该类的 public 方法都配置相同的事务属性信息。
- 作用在方法:当类配置了@Transactional,方法也配置了@Transactional,方法的事务会覆盖类的事务配置信息。
- 作用于接口:不推荐这种使用方法,因为一旦标注在Interface上并且配置了Spring AOP 使用CGLib动态代理,将会导致@Transactional注解失效。
需要特别注意的是,此@Transactional注解来自org.springframework.transaction.annotation包,而不是javax.transaction。
@Transactional
注解中常用参数:
value
:当在配置文件中有多个 TransactionManager,可以用该属性指定选择哪个事务管理器。propagation
:事务的传播行为,默认值为 REQUIRED。isolation
:事务的隔离级别,默认值为 DEFAULT,即采用数据库的默认隔离级别。timeout
:事务的超时时间(单位是秒),默认值为 -1。如果超过该时间限制但事务还未提交,则自动回滚事务。readOnly
:用于指定事务是否为只读事务,默认值为 false。为了忽略那些不需要事务的方法,比如select读取数据,可以设置readOnly = true。rollbackFor
:指定能够触发事务回滚的异常类型,可以指定多个异常类型。noRollbackFor
:指定不用回滚事务的异常类型,可以指定多个异常类型。
四、事务的传播行为
事务的传播机制一般用在事务的嵌套中,当事务方法被另一个事务方法调用时,则应该指定事务如何传播。比如事务方法A直接或间接调用了方法B,那么这两个方法是各自作为独立的方法提交,还是内层的事务合并到外层的事务一起提交,这就是需要事务传播机制的配置来确定怎么样执行。
注:事务的传播行为和隔离级别都定义在TransactionDefinition接口中:
1 | int PROPAGATION_REQUIRED = 0; |
事务的传播行为如下表所示(主要学习前两个即可,其它的简单了解):
事务传播行为 | 描述 |
---|---|
PROPAGATION_REQUIRED | 支持外层事务。这是Spring默认的传播机制,能满足绝大部分业务需求,如果外层有事务,则当前事务加入到外层事务,一块提交,一块回滚。如果外层没有事务,则创建一个新的事务。 |
PROPAGATION_REQUIRES_NEW | 不支持外层事务。该事务传播机制是每次都会新开启一个事务,同时把外层事务挂起,当前事务执行完毕,恢复上层事务的执行。如果外层没有事务,执行当前新开启的事务即可。 |
PROPAGATION_SUPPORTS | 支持外层事务。如果外层有事务,则加入外层事务,如果外层没有事务,则直接使用非事务方式执行。完全依赖外层的事务 |
PROPAGATION_NOT_SUPPORTED | 不支持外层事务。该传播机制不支持事务,如果外层存在事务则挂起,执行完当前代码,则恢复外层事务,无论是否异常都不会回滚当前的代码 |
PROPAGATION_NEVER | 不支持外层事务。该传播机制不支持外层事务,即如果外层有事务就抛出异常 |
PROPAGATION_MANDATORY | 支持外层事务。与NEVER相反,如果外层没有事务,则抛出异常 |
PROPAGATION_NESTED | Spring 所特有的。该传播机制的特点是可以保存状态保存点,当前事务回滚到某一个点,从而避免所有的嵌套事务都回滚,即各自回滚各自的,如果子事务没有把异常吃掉,基本还是会引起全部回滚,等价于TransactionDefinition.PROPAGATION_REQUIRED。 |
五、事务的只读属性和超时属性
①、只读属性
一个事务如果是做查询操作,可以设置为只读,此时数据库可以针对查询操作来做优化,有利于提高性能。
1 |
|
如果是针对增删改方法设置只读属性,则会抛出下面异常:
1 | 表面的异常信息:TransientDataAccessResourceException: PreparedStatementCallback |
实际开发时建议把查询操作设置为只读。
②、超时属性
一个数据库操作有可能因为网络或死锁等问题卡住很长时间,从而导致数据库连接等资源一直处于被占用的状态。所以我们可以设置一个超时属性,让一个事务执行太长时间后,主动回滚。事务结束后把资源释放出来。
1 | //单位为秒 |