Spring声明式事务和编程式事务的原理及使用场景

当>=2张表需要同时增删改时,我们需要使用事务来实现操作的原子性、一致性。数据库的事务默认只支持单表的操作,如果有两个或以上的表需要同时修改时,需要设置事务为手动提交、回滚。Java、Spring在遵循数据库事务的基础上提供了代码层面的实现。

Spring事务的两种使用方式:

1、在方法上添加@Transactional注解启用声明式事务。

@Transactional
public int insertUser(SysUser user) {
    // a、新增用户信息
    int rows = userMapper.insert(user);
    // b、新增用户岗位关联
    insertUserPost(user);
    // c、新增用户与角色管理
    insertUserRole(user);
    return rows;
}

我们在insertUser()方法上添加@Transactional注解开启一个声明式事务,使方法内a、b、c三次DB操作在一个事务里。


2、使用TransactionTemplate类启动编程式事务。

transactionTemplate.execute(new TransactionCallback<Boolean>(){
	@Override
	public Boolean doInTransaction(TransactionStatus status) {
		// a、新增用户信息
	    int rows = userMapper.insert(user);
	    // b、新增用户岗位关联
	    insertUserPost(user);
	    // c、新增用户与角色管理
	    insertUserRole(user);
		return Boolean.TRUE;
	}
});

我们通过编写代码调用TransactionTemplate对象的execute()方法,开启一个编程式事务,使方法内a、b、c三次DB操作在一个事务里。


@Transactional声明式事务的原理

@Transactional注解的本质是通过动态代理实现对被代理方法无侵入性的增强。JdkDynamicAopProxy是Spring所有切面AOP的入口,

final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {
	// ...
	@Override
	@Nullable
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		// ...
		// Get the interception chain for this method.
		List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
		// ...

	}

在invoke()中通过责任链模式加载相关的Interceptor拦截器对方法进行增强,

public class AdvisedSupport extends ProxyConfig implements Advised {
	// ...
	/** Cache with Method as key and advisor chain List as value. */
	private transient Map<MethodCacheKey, List<Object>> methodCache;
	// ...

	public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, @Nullable Class<?> targetClass) {
		MethodCacheKey cacheKey = new MethodCacheKey(method);
		List<Object> cached = this.methodCache.get(cacheKey);
		if (cached == null) {
			cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
					this, method, targetClass);
			this.methodCache.put(cacheKey, cached);
		}
		return cached;
	}
	// ...
}

比如加了@Transactional注解的方法,会通过TransactionInterceptor拦截器对方法添加事务的增强。


TransactionTemplate编程式事务的使用场景

@Transactional方法级别的事务将整个方法视为一个事务,在方法执行的过程中持有Connection连接,直到事务方法执行完成才释放连接。如果事务方法体内有操作耗时的查询操作或远程调用操作时,就会造成Connection连接长时间不释放,大大减少了连接的复用,在高并发的场景下这是致命的。于是,我们需要缩小事务的控制粒度。

我们可以使用TransactionTemplate类把需要事务的操作放入execute()方法的代码块内,耗时的查询操作或远程调用操作放在事务外。以此来缩短占用Connection连接的时间。

代码比较简单,这里就不列举实例了。


TransactionTemplate编程式事务的两种使用方式

1、有返回参数的使用方式:

public class TransactionTemplate extends DefaultTransactionDefinition
		implements TransactionOperations, InitializingBean {
	// ...
	@Override
	@Nullable
	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 {
			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;
		}
	}
	// ...
}

文章开头的示例,就使用的是这种方式。

execute(TransactionCallback action)的参数TransactionCallback是一个函数接口,

@FunctionalInterface
public interface TransactionCallback<T> {
	@Nullable
	T doInTransaction(TransactionStatus status);
}

所以,使用时我们可以利用JDK8的新特性,简写成:

	Boolean flag = transactionTemplate.execute((status) -> {
		// a、新增用户信息
	    int rows = userMapper.insert(user);
	    // b、新增用户岗位关联
	    insertUserPost(user);
	    // c、新增用户与角色管理
	    insertUserRole(user);
		return Boolean.TRUE;
});

2、无返回参数的使用方式:

TransactionCallback有一个实现类TransactionCallbackWithoutResult。该抽象类增加了一个无返回参数的抽象方法doInTransactionWithoutResult();实现接口的doInTransaction()方法被final修饰,在调用doInTransactionWithoutResult()方法后返回null。最终实现了无返回参数的扩展。

public abstract class TransactionCallbackWithoutResult implements TransactionCallback<Object> {

	@Override
	@Nullable
	public final Object doInTransaction(TransactionStatus status) {
		doInTransactionWithoutResult(status);
		return null;
	}
	protected abstract void doInTransactionWithoutResult(TransactionStatus status);

}

至此,本篇完。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值