一、事务传播
1、PROPAGATION_REQUIRED:
这个是最常见的,就是说,如果ServiceA.method调用了ServiceB.method,如果ServiceA.method开启了事务,然后ServiceB.method也声明了事务,那么ServiceB.method不会开启独立事务,而是将自己的操作放在ServiceA.method的事务中来执行,ServiceA和ServiceB任何一个报错都会导致整个事务回滚。这就是默认的行为,其实一般我们都是需要这样子的。
2、PROPAGATION_SUPPORTS:
如果ServiceA.method开了事务,那么ServiceB就将自己加入ServiceA中来运行,如果ServiceA.method没有开事务,那么ServiceB自己也不开事务
3、PROPAGATION_MANDATORY:
必须被一个开启了事务的方法来调用自己,否则报错
4、PROPAGATION_REQUIRES_NEW:
ServiceB.method强制性自己开启一个新的事务,然后ServiceA.method的事务会卡住,等ServiceB事务完了自己再继续。这就是影响的回滚了,如果ServiceA报错了,ServiceB是不会受到影响的,ServiceB报错了,ServiceA也可以选择性的回滚或者是提交。
5、PROPAGATION_NOT_SUPPORTED:
就是ServiceB.method不支持事务,ServiceA的事务执行到ServiceB那儿,就挂起来了,ServiceB用非事务方式运行结束,ServiceA事务再继续运行。这个好处就是ServiceB代码报错不会让ServiceA回滚。
6、PROPAGATION_NEVER:
不能被一个事务来调用,ServiceA.method开事务了,但是调用了ServiceB会报错
7、PROPAGATION_NESTED:
开启嵌套事务,ServiceB开启一个子事务,如果回滚的话,那么ServiceB就回滚到开启子事务的这个save point。
其实就是ServiceA里循环51调用ServiceB,第51次调用ServiceB失败了。第一个选项,就是两个事务都设置为PROPAGATION_REQUIRED就好了,ServiceB的所有操作都加入了ServiceA启动的一个大事务里去,任何一次失败都会导致整个事务的回滚;第二个选项,就是将ServiceB设置为PROPAGATION_REQUIRES_NEW,这样ServiceB的每次调用都在一个独立的事务里执行,这样的话,即使第51次报错,但是仅仅只是回滚第51次的操作,前面50次都在独立的事务里成功了,是不会回滚的。
其实一般也就PROPAGATION_REQUIRES_NEW比较常用,要的效果就是嵌套的那个事务是独立的事务,自己提交或者回滚,不影响外面的大事务,外面的大事务可以获取抛出的异常,自己决定是继续提交大事务还是回滚大事务。
二、AOP 动态代理的事务支持
其实核心的话,就是由 Controller 去调用 Service 的时候,调用的是 动态代理对象,通过标记 @Transactional 注解,通过 AOP 织入一些事务的代码。
首先开启一个事务,将要执行的逻辑都包含在事务当中,然后去执行逻辑,如果出错的话,执行 mysql 的回滚操作,如果执行成功的话,就执行提交。
三、Spring 事务源码
spring-tx 包就是事务的源码,核心就是 org.springframework.transaction.interceptor 这个包下的内容,TransactionInterceptor 这个类是核心入口,也就是说,如果我们加了 @Transactional 之后,就会进行拦截,先走这个类的方法。
public Object invoke(final 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);
// 这个方法的话就是核心的方法
return invokeWithinTransaction(invocation.getMethod(), targetClass, new InvocationCallback() {
@Override
public Object proceedWithInvocation() throws Throwable {
return invocation.proceed();
}
});
}
复制代码
3.1 invokeWithinTransaction() 入口
protected Object invokeWithinTransaction(Method method, Class<?> targetClass, final InvocationCallback invocation)
throws Throwable {
// 初始化一些对象实例
final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);
final PlatformTransactionManager tm = determineTransactionManager(txAttr);
final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
// Standard transaction demarcation with getTransaction and commit/rollback calls.
// 如果标记了 @Transactional 注解,就创建一个事务对象
TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
Object retVal = null;
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;
}
... ... ... ... ...
}
复制代码
3.2 createTransactionIfNecessary() 开启事务
protected TransactionInfo createTransactionIfNecessary(
PlatformTransactionManager tm, TransactionAttribute txAttr, final String joinpointIdentification) {
// If no name specified, apply method identification as transaction name.
if (txAttr != null && txAttr.getName() == null) {
txAttr = new DelegatingTransactionAttribute(txAttr) {
@Override
public String getName() {
return joinpointIdentification;
}
};
}
TransactionStatus status = null;
if (txAttr != null) {
if (tm != null) {
status = tm.getTransaction(txAttr);
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Skipping transactional joinpoint [" + joinpointIdentification +
"] because no transaction manager has been configured");
}
}
}
return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
}
复制代码
这个开启事务的方法,太深入了,主要的流程的话,就是通过 hibernate-entitymanager 项目和 hibernate-core 项目,去进行开启,它俩底层也是在调用 jdbc ,通过我们配置的 Druid 链接池信息,获取到 Connection 对象之后,通过 setAutocommid(false) 最终开启了一个事务。
3.3 事务开启后,执行业务逻辑
这个还是很简单的,没有什么可看的,就是通过调用拦截器的 invoke() 方法,调用相关服务的方法,去执行我们的逻辑。
3.4 事务提交
protected void commitTransactionAfterReturning(TransactionInfo txInfo) {
if (txInfo != null && txInfo.hasTransaction()) {
if (logger.isTraceEnabled()) {
logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() + "]");
}
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
}
复制代码
这个其实和上面是一样的,最终也是通过 hibernate 调用 jdbc 进行 commit
3.4 事务回滚
事务的主要原理,就是通过 TransactionInterceptor 这个拦截器的 invoke() 方法,这个主入口,对其进行一系列的控制,首先就是先创建一个 Transaction 对象,通过调用 Hibernate 框架的相关 jar 包在,最终通过 jdbc 获取到 Connection 对象,通过这个对象设置 AutoCommit 属性为 false , 开启一个事务。
然后会执行我们写的业务逻辑,如果逻辑执行正常,没有报错的话,最终还是会通过 Hibernate 调用 jdbc 底层,通过 Connection.commit() 进行事务提交, 如果执行过程中出现异常的话,最终也是通过 Hibernate 调用 jdbc 底层,执行 Connection.rollback() 进行事务的回滚操作。