下面除了第6节编程式事务,其他都是指声明式事务。
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()
:回滚事务<!-- 声明式事务依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>6.0.6</version>
</dependency>
@EnableTransactionManagement
/**
* 装配事务管理实现对象
* @param dataSource
* @return
*/
@Bean
public TransactionManager transactionManager(DataSource dataSource){
return new DataSourceTransactionManager(dataSource);
}
@Transactional
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 则只能做读操作,用于数据库针对查询优化,一般不使用
@EnableTransactionManagement
public
权限修饰的方法才支持声明式事务final
或 static
修饰 :spring 事务底层用了 jdk 动态代理或者 cglib 生成代理类,这两个关键字将导致重写代理类的该方法myisam
或其他不支持事务的引擎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 方法不会生成事务。
修改方式:
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();
}
}
propagation = Propagation.NEVER
rollbackFor
定义的异常类及其子类的异常时,事务将不会回滚通常情况下,我们会在方法上加 @Transactional
注解,添加事务功能,比如:
@Transactional
public void add(UserModel userModel) throws Exception {
query1();
query2();
query3();
roleService.save(userModel);
update(userModel);
}
但 @Transactional
注解,如果被加到方法上,有个缺点就是整个方法都包含在事务当中了。上面的这个例子中,在 UserService
类中,其实只有这两行才需要事务,但是这种写法会导致所有的 query()
方法也被包含在同一个事务中。如果 query 方法非常多,调用层级很深,而且有部分查询方法比较耗时的话,会造成整个事务非常耗时,而从造成大事务问题。
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()
方法进行回滚