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);

}

至此,本篇完。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring框架提供了两种事务管理方编程事务管理和声明式事务管理。 1. 编程事务管理: 编程事务管理是通过编写代码来管理事务的提交和回滚。在这种方下,开发人员需要手动编写事务的开始、提交和回滚的代码。Spring提供了`TransactionTemplate`和`TransactionDefinition`等类来简化编程事务管理的操作。通过使用`TransactionTemplate`,可以在需要进行事务处理的代码块中对事务进行管理。 2. 声明式事务管理: 声明式事务管理是通过配置的方来管理事务的提交和回滚,而不需要手动编写事务管理的代码。在这种方下,开发人员只需要在需要进行事务处理的方法上使用注解或者XML配置文件来声明事务的属性,Spring框架就会根据配置自动实现事务管理。常见的注解方使用`@Transactional`注解。 两种事务管理方各有优劣,编程事务管理灵活性较高,适用于复杂的事务场景,但需要开发人员手动编写大量的事务管理代码;声明式事务管理简化了开发工作,通过配置即可实现事务管理,但对于一些复杂的业务场景可能不够灵活。 总的来说,对于大部分应用场景而言,推荐使用声明式事务管理,可以减少重复代码的编写,提高开发效率。而在一些特殊的业务场景下,如需要动态控制事务的提交和回滚,或者需要手动处理一些特殊情况,可以考虑使用编程事务管理。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值