【Spring】Spring 事务
Spring 事务
1. 简介
- 编程式事务:指手动编写程序来管理事务,即通过编写代码的方式直接控制事务的提交和回滚。主要优点是灵活性高,可以按照自己的需求来控制事务的粒度、模式等等。但是,一般编写大量的事务控制代码容易出现问题,对代码的可读性和可维护性有一定影响。
- 声明式事务:指使用注解或 XML 配置的方式来控制事务的提交和回滚。
下面除了第6节编程式事务,其他都是指声明式事务。
2. Spring事务管理器
Spring声明式事务对应依赖
spring-tx
:包含声明式事务实现的基本规范(事务管理器规范接口和事务增强等等)spring-jdbc
:包含DataSource
方式事务管理器实现类DataSourceTransactionManager
spring-orm
:包含其他持久层框架的事务管理器实现类例如:Hibernate/Jpa
等
Spring声明式事务对应事务管理器接口:
较常使用的事务管理器是 org.springframework.jdbc.datasource.DataSourceTransactionManager
,将来整合 JDBC方式、JdbcTemplate方式、Mybatis方式的事务实现
DataSourceTransactionManager类中的主要方法:
doBegin()
:开启事务doSuspend()
:挂起事务doResume()
:恢复挂起的事务doCommit()
:提交事务doRollback()
:回滚事务
3. 基本使用
- 添加依赖
<!-- 声明式事务依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>6.0.6</version>
</dependency>
- 开启事务注解:
@EnableTransactionManagement
- 添加事务管理器到 IoC 容器中
/**
* 装配事务管理实现对象
* @param dataSource
* @return
*/
@Bean
public TransactionManager transactionManager(DataSource dataSource){
return new DataSourceTransactionManager(dataSource);
}
- 在类上或方法上使用
@Transactional
- 在类上使用,则会影响到类中的每个方法同时,类级别标记的 @Transactional 注解中设置的事务属性也会延续影响到方法执行时的事务属性。除非在方法上又设置了 @Transactional 注解。对一个方法来说,离它最近的 @Transactional 注解中的事务属性设置生效。
4. 属性剖析
-
timeout
:默认 -1 ,表示永不超时,超时则会事务回滚 -
rollbackFor
:表示遇到属于该异常的类进行事务回滚,默认遇到 RuntimeException 及其子类和 Error 及其子类时才会回滚
,建议设置rollbackFor = Exception.class
或者Throwable.class
表示Exception及其子类
或Throwable
的异常都会触发回滚,同时不影响Error的回滚 -
propagation
:事务的传播行为,默认Propagation.REQUIRED
,一共有七种,一般只使用前两种事务传播行为类型 说明 REQUIRED 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见也是默认的选择。 REQUIRES_NEW 新建事务,如果当前存在事务,把当前事务挂起。 SUPPORTS 支持当前事务,如果当前没有事务,就以非事务方式执行。 MANDATORY 使用当前的事务,如果当前没有事务,就抛出异常。 NOT_SUPPORTED 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 NEVER 以非事务方式执行,如果当前存在事务,则抛出异常。 NESTED 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与 REQUIRED
类似的操作 -
isolation
:用于设置事务的隔离级别,Isolation.DEFAULT
,一般不修改 -
readOnly
:默认 false,设置为 true 则只能做读操作,用于数据库针对查询优化,一般不使用
5. 声明式事务问题场景
5.1 事务不生效
- 未开启事务 :未使用开启事务注解
@EnableTransactionManagement
- 未被Spring管理 :被代理类需被Spring管理才能使用 AOP 功能
- 访问权限问题 :只有
public
权限修饰的方法才支持声明式事务 - 方法用
final
或static
修饰 :spring 事务底层用了 jdk 动态代理或者 cglib 生成代理类,这两个关键字将导致重写代理类的该方法 - 多线程调用 :不同线程获取到的数据库连接不一样,从而必然是两个不同的事务
- 表不支持事务 :数据库表引擎是
myisam
或其他不支持事务的引擎 - 方法内调用另一个同类方法:如下,add方法中调用了事务方法
updateStatus()
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public void add(UserModel userModel) {
userMapper.insertUser(userModel);
updateStatus(userModel);
}
@Transactional
public void updateStatus(UserModel userModel) {
doSameThing();
}
}
updateStatus()
方法拥有事务的能力是因为 spring aop
生成代理了对象,但这种方式调用了 this 对象的方法,所以 updateStatus 方法不会生成事务。
修改方式:
- 新加一个 Service 将方法中挪到新的 Service 中,通过新 Service 调用
- 在该 Service 中注入自己,再将调用改为
userService.updateStatus(userModel)
- 使用
AopContext.currentProxy()
调用,即改为((UserService)AopContext.currentProxy()).updateStatus(userModel)
,推荐
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public void add(UserModel userModel) {
userMapper.insertUser(userModel);
((UserService)AopContext.currentProxy()).updateStatus(userModel)
}
@Transactional
public void updateStatus(UserModel userModel) {
doSameThing();
}
}
5.2 事务不回滚
- 错误的传播机制 :设置了
propagation = Propagation.NEVER
- try…catch…吞了异常:在 catch 中不抛出任何异常或抛了别的异常 spring 事务也不会回滚
- rollbackFor参数不合理 :当发生了不属于
rollbackFor
定义的异常类及其子类的异常时,事务将不会回滚
5.3 大事务问题
通常情况下,我们会在方法上加 @Transactional
注解,添加事务功能,比如:
@Transactional
public void add(UserModel userModel) throws Exception {
query1();
query2();
query3();
roleService.save(userModel);
update(userModel);
}
但 @Transactional
注解,如果被加到方法上,有个缺点就是整个方法都包含在事务当中了。上面的这个例子中,在 UserService
类中,其实只有这两行才需要事务,但是这种写法会导致所有的 query()
方法也被包含在同一个事务中。如果 query 方法非常多,调用层级很深,而且有部分查询方法比较耗时的话,会造成整个事务非常耗时,而从造成大事务问题。
6. 编程式事务
5中所提到的问题都是声明式事务的,一般建议少使用声明式事务,但并不是说一定不能用它,如果项目中有些业务逻辑比较简单,而且不经常变动,使用 @Transactional
注解开启事务也无妨,因为它更简单,开发效率更高,但是千万要小心事务失效的问题。大项目更建议使用基于 TransactionTemplate
的编程式事务,即通过手动编写代码实现的事务。
基于 TransactionTemplate
模板的编程式事务的使用 :
- 注入
TransactionTemplate
模板到 IoC 容器中
@Bean
public DataSource dataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUsername(username);
dataSource.setPassword(password);
dataSource.setUrl(url);
dataSource.setDriverClassName(driver);
return dataSource;
}
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
@Bean
public TransactionTemplate transactionTemplate(PlatformTransactionManager platformTransactionManager) {
return new TransactionTemplate(platformTransactionManager);
}
- 注入到需要使用的类中,并使用如下两个方法之一
execute(TransactionCallback<T> action)有返回值
executeWithoutResult(Consumer<TransactionStatus> action) 无返回值
@Autowize
private TransactionTemplate transactionTemplate;
@Transactional
public void add(UserModel userModel) throws Exception {
query1();
query2();
query3();
transactionTemplate.executeWithoutResult((status -> {
try {
roleService.save(userModel);
update(userModel);
} catch (Exception e) {
status.setRollbackOnly();
}
}));
}
- 异常调用
status.setRollbackOnly()
方法进行回滚
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!