当>=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);
}
至此,本篇完。