如何控制事务
本质就是这几句话:
JDBC:
Connection.setAutoCommit(false);
Connection.commit();–提交
Connection.rollback();–回滚
Spring实现事务的步骤:
那我们基于Spring ,在业务里想加一个事务,结合上一篇我们说的AOP 的形式,就可以把上边这代码写在业务逻辑处理之后和处理异常的情况里。这样不就完成事务了么。
前提:
事务和数据库相关,首先得是调用Dao 层。也就是我们原始对象部分:
- 原始对象 —》 原始⽅法 —》核⼼功能 (业务处理+DAO调⽤)
- DAO作为Service的成员变量,依赖注⼊的⽅式进⾏赋值
public class XXXUserServiceImpl{
private xxxDAO xxxDAO
set get
}
步骤一:额外功能部分:
方式 1) 手写MethodInterceptor : (仅推演-实际不用这样写了)
ppublic Object invoke(MethodInvocation invocation){
try{
Connection.setAutoCommit(false);
Object ret = invocation.proceed();
Connection.commit();
}catch(Exception e){
Connection.rollback();
}
return ret;
}
既然所有的业务逻辑事务都是可以固定这样写,那是不是可以直接封装在框架里就行了。Spring 的确是这么做的。
方式 2) Sring的封装 (这一部分引入Spring 事务部分就有了。不用单独写)
它封装在了 org.springframework.jdbc.datasource.DataSourceTransactionManager。核心还是上面这几行代码。
步骤二:切入点,通过注解
@Transactional 事务的额外功能加⼊给那些业务⽅法。
- 如果加在类上:类中所有的⽅法都会加⼊事务
- 如果加在⽅法上:这个⽅法会加⼊事务
步骤三:组装切面
组装切面是连接切入点和额外功能。
Spring 封装了对应的标签,是通过标签这样做的。
<tx:annotation-driven transaction-manager=""/>
那这句话是怎么联系起来的呢?首先有了这个标签,Spring 会通过扫描所有 @Transactional 的注解作为切入点。其次,transaction-manager 这个属性会把步骤一里的额外功能(DataSourceTransactionManager在这个包里)扫描进去。这不就齐活了。
传播属性
描述了事务解决嵌套问题的特征
保证在同一个时间里在嵌套里最多只有一个事务。
required: 有就支持,没有就单开。
support:纯支持,有就支持,没有也不开。
required_new :要有新的。有就挂起外部的,创建新的,没有就直接创建新的。
特殊注意:
抛出异常时默认情况:
默认 对于RuntimeException及其⼦类 采⽤的是回滚的策略
默认 对于Exception及其⼦类 采⽤的是提交的策略
那如果我们想针对抛出 Exception类,改成用回顾可以么。这就需要设置这两个属性:
rollbackFor = {java.lang.Exception,xxx,xxx} --设置回滚
noRollbackFor = {java.lang.RuntimeException,xxx,xx} --设置不回滚
@Transactional(rollbackFor = {java.lang.Exception.class},noRollbackFor
= {java.lang.RuntimeException.class})
建议:实战中使⽤RuntimeExceptin及其⼦类 使⽤事务异常属性的默认值
事务属性常见配置
- 隔离属性 默认值
- 传播属性 Required(默认值) 增删改 Supports 查询操作
- 只读属性 readOnly false 增删改 true 查询操作
- 超时属性 默认值 -1
- 异常属性 默认值
即常见配置:
增删改操作 @Transactional
查询操作
@Transactional(propagation=Propagation.SUPPORTS,readOnly=true)
源码解析 -
前置知识参考:Spring源码解析之-- 事务注解 处理流程 分析
1) 为什么加了 Transactional 这个注解,就会有事务
从这个 TransactionInterceptor 入口开始看
1.TransactioanIntercepor.invoke()
TransactionInterceptor 会对我们添加了 @Transactional 注解的方法或者类进行拦截,之后就会在执行业务逻辑之前先调用该类。
@Override
@Nullable
public Object invoke(MethodInvocation invocation) throws Throwable {
// Work out the target class: may be {@code null}.
// The TransactionAttributeSource should be passed the target class
// as well as the method, which may be from an interface.
Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
// Adapt to TransactionAspectSupport's invokeWithinTransaction...
return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
}
2.TransactionAspectSupport.invokeWithinTransaction
通过类名xxxAspectxxx,我们可以猜到这是一个Aop的代理类。
下面这段逻辑是核心,包含了事务的开启,提交,回滚等
if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
// Standard transaction demarcation with getTransaction and commit/rollback calls.
TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
Object retVal;
try {
// This is an around advice: Invoke the next interceptor in the chain.
// This will normally result in a target object being invoked.
// 调用目标类,即代理类执行具体业务逻辑
retVal = invocation.proceedWithInvocation();
}
catch (Throwable ex) {
// target invocation exception
// 目标调用异常,进行回滚
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
cleanupTransactionInfo(txInfo);
}
// 进行提交
commitTransactionAfterReturning(txInfo);
return retVal;
}
else {
...
}
事务传播属性
那接下来我们来看下事务传播属性是怎么通过配置就生效了呢。在事务执行过程中,我们配置 Propagation= Required 后执行的过程发生了什么?
这部分后续再讲