声明式事务的一些缺点:

  • 作用于方法上,颗粒度较大,无法作用于代码块。
  • 无法实现较为复杂的事务逻辑控制。
  • 新开事务时会将原来的事务进行挂起,浪费连接资源,如果这个线程在执行耗时的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);
	}
}