声明式事务的一些缺点:
- 作用于方法上,颗粒度较大,无法作用于代码块。
- 无法实现较为复杂的事务逻辑控制。
- 新开事务时会将原来的事务进行挂起,浪费连接资源,如果这个线程在执行耗时的IO操作,那么就会一直占用多个连接资源。
所以Spring提供了模板类TransactionTemplate以手动编程的方式来管理事务。
编程式事务的使用
配置类
需要注入TransactionTemplate,无需@EnableTransactionManagement。
@Bean
public TransactionTemplate transactionTemplate() {
return new TransactionTemplate(transactionManager());
}
service
package com.morris.spring.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.support.TransactionTemplate;
@Component
public class ProgrammaticTransactionService {
@Autowired
private GoodService goodService;
@Autowired
private AreaService areaService;
@Autowired
private TransactionTemplate transactionTemplate;
public void addGoodAndArea() {
transactionTemplate.execute((status)-> goodService.addGood());
transactionTemplate.execute((status)-> areaService.addArea());
}
}
execute()执行完后会自动提交事务。
源码分析
org.springframework.transaction.support.TransactionTemplate#execute
public <T> T execute(TransactionCallback<T> action) throws TransactionException {
Assert.state(this.transactionManager != null, "No PlatformTransactionManager set");
if (this.transactionManager instanceof CallbackPreferringPlatformTransactionManager) {
return ((CallbackPreferringPlatformTransactionManager) this.transactionManager).execute(this, action);
}
else {
/**
* @see AbstractPlatformTransactionManager#getTransaction(org.springframework.transaction.TransactionDefinition)
*/
TransactionStatus status = this.transactionManager.getTransaction(this);
T result;
try {
// 回调
result = action.doInTransaction(status);
}
catch (RuntimeException | Error ex) {
// Transactional code threw application exception -> rollback
rollbackOnException(status, ex); // 回滚
throw ex;
}
catch (Throwable ex) {
// Transactional code threw unexpected exception -> rollback
rollbackOnException(status, ex);
throw new UndeclaredThrowableException(ex, "TransactionCallback threw undeclared checked exception");
}
this.transactionManager.commit(status); // 提交事务
return result;
}
}
监听事务的生命周期
Spring提供了TransactionSynchronizationManager来获取当前事务的状态,数据库的连接、隔离级别,以及提供了一系列的钩子方法来监控事务的整个生命周期。
TransactionSynchronizationManager不仅可以用于编程式事务,还可以用于声明式事务。
public void addGoodAndArea2() {
transactionTemplate.execute((status)-> {
// 获得真正的连接对象
ConnectionHolder connectionHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
@Override
public void beforeCommit(boolean readOnly) {
System.out.println("beforeCommit");
}
@Override
public void beforeCompletion() {
System.out.println("beforeCompletion");
}
@Override
public void afterCommit() {
System.out.println("afterCommit");
}
@Override
public void afterCompletion(int status) {
System.out.println("afterCompletion");
}
});
goodService.addGood();
areaService.addArea();
return null;
});
}
运行结果如下:
DEBUG DataSourceTransactionManager:381 - Creating new transaction with name [null]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
DEBUG DriverManagerDataSource:144 - Creating new JDBC DriverManager Connection to [jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&allowMultiQueries=true&characterEncoding=UTF-8&useFastDateParsing=false&zeroDateTimeBehavior=convertToNull]
DEBUG DataSourceTransactionManager:265 - Acquired Connection [com.mysql.cj.jdbc.ConnectionImpl@1b266b3] for JDBC transaction
DEBUG DataSourceTransactionManager:283 - Switching JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1b266b3] to manual commit
DEBUG JdbcTemplate:860 - Executing prepared SQL update
DEBUG JdbcTemplate:609 - Executing prepared SQL statement [insert into t_good(good_name, price) values(?,?)]
DEBUG JdbcTemplate:860 - Executing prepared SQL update
DEBUG JdbcTemplate:609 - Executing prepared SQL statement [insert into t_area(area_name, area_code) values(?,?)]
beforeCommit
beforeCompletion
DEBUG DataSourceTransactionManager:763 - Initiating transaction commit
DEBUG DataSourceTransactionManager:329 - Committing JDBC transaction on Connection [com.mysql.cj.jdbc.ConnectionImpl@1b266b3]
afterCommit
afterCompletion
DEBUG DataSourceTransactionManager:390 - Releasing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1b266b3] after transaction
手动管理事务
public void addGoodAndArea3() {
PlatformTransactionManager transactionManager = transactionTemplate.getTransactionManager();
// DefaultTransactionDefinition默认传播属性PROPAGATION_REQUIRED
TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
try {
goodService.addGood();
areaService.addArea(10);
} catch (Exception e) {
e.printStackTrace();
transactionManager.rollback(status);
}
finally {
transactionManager.commit(status);
}
}