本篇文章主要关注点,spring同类调用事务是否失效、spring开启事务、spring事务如何传播、spring事务失效场景、srpng事务如何回滚及回滚几次、spring事务挂起如何实现代表什么。如何看spring源码?spring源码规模很大,我们如何看?反正我是没有耐心看的,我调试源码的目的是找入口和相关类。然后再项目中断点哪个类调试的方式找到调用逻辑关系。这种方式看源码好处,代码调用逻辑链容易看出,调试中间变量容易看出。看源码只关心自己关心的部分而不是全部。
一、spring事务的本质
了解spring事务如何传播的的前提是了解spring实现事务的原理。spring是如何实现事务的?
1.1、spring本质不支持事务
spring事务的本质其实就是数据库对事务的支持,没有数据库的事务支持,spring是无法提供事务功能的。对于纯JDBC操作数据库,想要用到事务可以按照下面代码的写法。 总结:spring本质是不支持事务的,只是控制事务提交回滚的时机。事务本质是什么?事务的本质就是 Connection、Commit、Rollback
private Connection conn = null;
private PreparedStatement ps = null;
try {
conn.setAutoCommit(false); //将自动提交设置为false
ps.executeUpdate("修改SQL"); //执行修改操作
ps.executeQuery("查询SQL"); //执行查询操作
ps.executeUpdate("删除SQL"); //执行删除操作
conn.commit(); //当两个操作成功后手动提交
} catch (Exception e) {
conn.rollback(); //一旦其中一个操作出错都将回滚,使三个操作都不成功
e.printStackTrace();
}
1.2、spring是如何控制事务提交回滚
1.2.1、spring如何管理事务的?
spring在控制事务提交和回滚的前提是spring必须能管理事务。spring如何实现对事务的管理?答案就是spring aop技术。
1.2.1.1、事务扫描配置基于注解
思路:对注解打标的方法拦截
<!-- 定义事务管理器 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!--使用注释事务 -->
<tx:annotation-driven transaction-manager="transactionManager" />
@Transactional(propagation=Propagation.REQUIRED,rollbackFor=Exception.class,timeout=1,isolation=Isolation.DEFAULT)
public void saveUser(Map<String, String> map) throws Exception {
System.out.println("方法开始");
for (int i = 0; i < 500000; i++) {
System.out.println("*");
}
System.out.println("进入保存");
userDao.saveUser(map);
System.out.println("退出保存");
}
1.2.1.2、事务扫描配置基于路径扫描配置
**思路:**对配置了扫描路径的方法且匹配了前缀的方法拦截。
<!-- 定义事务管理器 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 下面使用aop切面的方式来实现 -->
<tx:advice id="TestAdvice" transaction-manager="transactionManager">
<!--配置事务传播性,隔离级别以及超时回滚等问题 -->
<tx:attributes>
<tx:method name="save*" propagation="REQUIRED" />
<tx:method name="del*" propagation="REQUIRED" />
<tx:method name="update*" propagation="REQUIRED" />
<tx:method name="add*" propagation="REQUIRED" />
<tx:method name="*" rollback-for="Exception" />
</tx:attributes>
</tx:advice>
<aop:config>
<!--配置事务切点 -->
<aop:pointcut id="services"
expression="execution(* com.website.service.*.*(..))" />
<aop:advisor pointcut-ref="services" advice-ref="TestAdvice" />
</aop:config>
1.3、spring如何管理事务的?
我们在上面搞懂spring事务的一个问题,spring是如何拦截需要事务处理的方法。在方法调用链中,spring如何将方法加入事务中,spring如何挂起事务,挂起是什么意识?spring如何知道什么时候提交回滚事务?
1.3.1、spring事务管理入口方法-事务拦截
//1、spring事务入口方法,利用AoP拦截注释方法 ReflectiveMethodInvocation.java
public Object proceed() throws Throwable {
// We start with an index of -1 and increment early.
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return invokeJoinpoint();
}
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
// Evaluate dynamic method matcher here: static part will already have
// been evaluated and found to match.
InterceptorAndDynamicMethodMatcher dm =
(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
return dm.interceptor.invoke(this);
}
else {
return proceed();
}
}
else {
// It's an interceptor, so we just invoke it: The pointcut will have
// been evaluated statically before this object was constructed.
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
1.3.2、spring如何开启一个事务
下面代码就是我从spring源码调试找到的,spring关闭自己提交的过程代码。
//spring关闭自动提交 DataSourceTransactionManager.java
protected void doBegin(Object transaction, TransactionDefinition definition) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
Connection con = null;
try {
if (!txObject.hasConnectionHolder() ||
txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
Connection newCon = this.dataSource.getConnection();
if (logger.isDebugEnabled()) {
logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");
}
txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
}
txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
con = txObject.getConnectionHolder().getConnection();
Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
txObject.setPreviousIsolationLevel(previousIsolationLevel);
//关闭自动提交改成手动提交,应为jdbc事务默认是开启自动提交的
if (con.getAutoCommit()) {
txObject.setMustRestoreAutoCommit(true);
if (logger.isDebugEnabled()) {
logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
}
con.setAutoCommit(false);
}
prepareTransactionalConnection(con, definition);
txObject.getConnectionHolder().setTransactionActive(true);
int timeout = determineTimeout(definition);
if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
}
// Bind the connection holder to the thread.
if (txObject.isNewConnectionHolder()) {
TransactionSynchronizationManager.bindResource(getDataSource(), txObject.getConnectionHolder());
}
}
catch (Throwable ex) {
if (txObject.isNewConnectionHolder()) {
DataSourceUtils.releaseConnection(con, this.dataSource);
txObject.setConnectionHolder(null, false);
}
throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex);
}
}
1.3.2、spring如何加入一个已存在的事务
在一个就是spring传播级别,比如当spring事务传播级别为PROPAGATION_REQUIRED,spring如何加入一个已经存在的事务?
//事务隔离级别检查 AbstractPlatformTransactionManager.java
@Override
public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
Object transaction = doGetTransaction();
// Cache debug flag to avoid repeated checks.
boolean debugEnabled = logger.isDebugEnabled();
if (definition == null) {
// Use defaults if no transaction definition given.
definition = new DefaultTransactionDefinition();
}
//此处就是事务传播行为的检查点,如果存在事务就不会走下面doBegin
if (isExistingTransaction(transaction)) {
// Existing transaction found -> check propagation behavior to find out how to behave.
return handleExistingTransaction(definition, transaction, debugEnabled);
}
// Check definition settings for new transaction.
if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout());
}
// 如果不存在事务就抛出异常
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
throw new IllegalTransactionStateException(
"No existing transaction found for transaction marked with propagation 'mandatory'");
}
//如果事务传播级别是REQUIRED、REQUIRES_NEW、NESTED如果不存在事务,则创建新事务
else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
SuspendedResourcesHolder suspendedResources = suspend(null);
if (debugEnabled) {
logger.debug("Creating new transaction with name [" + definition.getName() + "]: " + definition);
}
try {
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
DefaultTransactionStatus status = newTransactionStatus(
definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
doBegin(transaction, definition);
prepareSynchronization(status, definition);
return status;
}
catch (RuntimeException ex) {
resume(null, suspendedResources);
throw ex;
}
catch (Error err) {
resume(null, suspendedResources);
throw err;
}
}
else {
//事务传播级别不匹配,创建空事务
// Create "empty" transaction: no actual transaction, but potentially synchronization.
if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT && logger.isWarnEnabled()) {
logger.warn("Custom isolation level specified but no actual transaction initiated; " +
"isolation level will effectively be ignored: " + definition);
}
boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
return prepareTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null);
}
}
1.3.3、spring如何知道什么时候提交回滚事务?
还有个关注点就是spring何时提交或者回滚。从java aop可以知道,事务是在方法执行结束后执行的,spring方法链相互调用,那么spring如何判断事务应该提交?
1.3.3.1、如何找spring事务回滚入口
看到下面报错信息没有 org.springframework.transaction 打头的几个类就有spring回滚事务的入口。
at com.yst.tms.order.center.service.impl.DeliveryOrderServiceImpl$$FastClassBySpringCGLIB$$4c20e28c.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:736)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:283)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:671)
at com.yst.tms.order.center.service.impl.DeliveryOrderServiceImpl$$EnhancerBySpringCGLIB$$5a1f699e.saveOrder(<generated>)
1.3.3.2、如何找spring事务回滚入口类
spring回滚入口类 TransactionAspectSupport.invokeWithinTransaction.completeTransactionAfterThrowing这个方法。
protected Object invokeWithinTransaction(Method method, Class<?> targetClass, final InvocationCallback invocation)
throws Throwable {
// If the transaction attribute is null, the method is non-transactional.
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.
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) {
// 当事务方法执行出现异常,spring执行回滚入口
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
cleanupTransactionInfo(txInfo);
}
commitTransactionAfterReturning(txInfo);
return retVal;
}
else {
final ThrowableHolder throwableHolder = new ThrowableHolder();
// It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
try {
Object result = ((CallbackPreferringPlatformTransactionManager) tm).execute(txAttr,
new TransactionCallback<Object>() {
@Override
public Object doInTransaction(TransactionStatus status) {
TransactionInfo txInfo = prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
try {
return invocation.proceedWithInvocation();
}
catch (Throwable ex) {
if (txAttr.rollbackOn(ex)) {
// A RuntimeException: will lead to a rollback.
if (ex instanceof RuntimeException) {
throw (RuntimeException) ex;
}
else {
throw new ThrowableHolderException(ex);
}
}
else {
// A normal return value: will lead to a commit.
throwableHolder.throwable = ex;
return null;
}
}
finally {
cleanupTransactionInfo(txInfo);
}
}
});
// Check result state: It might indicate a Throwable to rethrow.
if (throwableHolder.throwable != null) {
throw throwableHolder.throwable;
}
return result;
}
catch (ThrowableHolderException ex) {
throw ex.getCause();
}
catch (TransactionSystemException ex2) {
if (throwableHolder.throwable != null) {
logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
ex2.initApplicationException(throwableHolder.throwable);
}
throw ex2;
}
catch (Throwable ex2) {
if (throwableHolder.throwable != null) {
logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
}
throw ex2;
}
}
}
1.3.3.3、spring事务回滚实现方法
在上面我们就分析spring没有事务功能,其利用mysql的事务功能,这个部分是spring事务回滚的最底层。其使用的是Connection.rollback。
//DataSourceTransactionManager.doRollback spring回滚实现类
@Override
protected void doRollback(DefaultTransactionStatus status) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
Connection con = txObject.getConnectionHolder().getConnection();
if (status.isDebug()) {
logger.debug("Rolling back JDBC transaction on Connection [" + con + "]");
}
try {
con.rollback();
}
catch (SQLException ex) {
throw new TransactionSystemException("Could not roll back JDBC transaction", ex);
}
}
1.3.3.4、方法嵌套调用spring事务回滚几次?
关于spring回滚几次分两种情况,子事务是否加入父事务、子事务是否独立事务。
**子事务加入父事务:**1次父事务
**子事务未加入父事务:**回滚次数子事务数+1次父事务
//如何证明 上面方法嵌套调用spring事务回滚几次? TransactionAspectSupport.invokeWithinTransaction
protected Object invokeWithinTransaction(Method method, Class<?> targetClass, final InvocationCallback invocation)
throws Throwable {
// If the transaction attribute is null, the method is non-transactional.
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.
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) {
// 此处打断点 关注joinpointIdentification变量类型的次数
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
cleanupTransactionInfo(txInfo);
}
commitTransactionAfterReturning(txInfo);
return retVal;
}
1.3.4、spring事务挂起实现
下面找到spring事务挂起的入口点及实现,spring事务挂起靠的就是重置Connection对象为空实现的**。**
//事务挂起入口分析 AbstractPlatformTransactionManager.java
private TransactionStatus handleExistingTransaction(
TransactionDefinition definition, Object transaction, boolean debugEnabled)
throws TransactionException {
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) {
throw new IllegalTransactionStateException(
"Existing transaction found for transaction marked with propagation 'never'");
}
//PROPAGATION_NOT_SUPPORTED事务挂起
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {
if (debugEnabled) {
logger.debug("Suspending current transaction");
}
Object suspendedResources = suspend(transaction);
boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
return prepareTransactionStatus(
definition, null, false, newSynchronization, debugEnabled, suspendedResources);
}
//REQUIRES_NEW事务挂起
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {
if (debugEnabled) {
logger.debug("Suspending current transaction, creating new transaction with name [" +
definition.getName() + "]");
}
SuspendedResourcesHolder suspendedResources = suspend(transaction);
try {
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
DefaultTransactionStatus status = newTransactionStatus(
definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
doBegin(transaction, definition);
prepareSynchronization(status, definition);
return status;
}
catch (RuntimeException beginEx) {
resumeAfterBeginException(transaction, suspendedResources, beginEx);
throw beginEx;
}
catch (Error beginErr) {
resumeAfterBeginException(transaction, suspendedResources, beginErr);
throw beginErr;
}
}
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
if (!isNestedTransactionAllowed()) {
throw new NestedTransactionNotSupportedException(
"Transaction manager does not allow nested transactions by default - " +
"specify 'nestedTransactionAllowed' property with value 'true'");
}
if (debugEnabled) {
logger.debug("Creating nested transaction with name [" + definition.getName() + "]");
}
if (useSavepointForNestedTransaction()) {
// Create savepoint within existing Spring-managed transaction,
// through the SavepointManager API implemented by TransactionStatus.
// Usually uses JDBC 3.0 savepoints. Never activates Spring synchronization.
DefaultTransactionStatus status =
prepareTransactionStatus(definition, transaction, false, false, debugEnabled, null);
status.createAndHoldSavepoint();
return status;
}
else {
// Nested transaction through nested begin and commit/rollback calls.
// Usually only for JTA: Spring synchronization might get activated here
// in case of a pre-existing JTA transaction.
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
DefaultTransactionStatus status = newTransactionStatus(
definition, transaction, true, newSynchronization, debugEnabled, null);
doBegin(transaction, definition);
prepareSynchronization(status, definition);
return status;
}
}
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
return prepareTransactionStatus(definition, transaction, false, newSynchronization, debugEnabled, null);
}
//事务挂起实现-将Connect连接置空 DataSourceTransactionManager.java
@Override
protected Object doSuspend(Object transaction) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
txObject.setConnectionHolder(null);
return TransactionSynchronizationManager.unbindResource(this.dataSource);
}
1.3.2、spring 同类方法之间调用事务为啥不生效?
1.3.2.1、演示spring同类方法调用不生效与生效代码写法
这点很多同学都不清楚吧,我们用事务的时候,时时刻要想到aop。1:写法走的是方法this之间的调用,没有用到动态代理。 2:写法走的是动态代理自然事务传播生效。但是,此时不生效不代表事务失败,this场景一样在事务里面。
public class StationServiceImpl extends BaseService implements StationService {
@Autowired
private StationService stationService;
@Override
@Transactional(rollbackFor = Exception.class)
public void closeStationById(Long stationId, String operateBy) {
Station station = stationMapper.selectById(stationId);
//1、这一样写事务传播不生效
getStations();
//2、这样写事务传播生效
stationService.getStationById();
//3、这样写事务传播生效 spring boot需要开启 @EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true),否则会报错:Cannot find current proxy: Set 'exposeProxy' property on Advised to 'true' to make it available
((StationService)AopContext.currentProxy()).getStationById(stationId);
}
@Override
@Transactional(rollbackFor = Exception.class)
public PagingResult<StationVO> getStations() {
userBindStationMapper.delete(new QueryWrapper<UserBindStation>().eq("station_id", 1L));
return null;
}
@Transactional(rollbackFor = Exception.class)
public StationDetailVO getStationById() {
userBindStationMapper.delete(new QueryWrapper<UserBindStation>().eq("station_id", 2L));
return null;
}
}
1.3.2.2、spring同类方法调用事务不生效代表事务无效吗?
上面我们介绍this写法,spring从调试源码的角度上讲事务没有起作用,但是不代表事务失败的。他依然在事务里面。spring事务的本质就是打开自动提交、提交、回滚。在此阶段整个语句依然在事务里面。但是如果入口父方法没有开启事务,this方式调B方法就算开启事务也会无效。
二、spring事务传播级别
2.1、spring事务传播级别列表整理
关于事务很多人都知道,但是spring事务传播级别能说明白的估计不多。啥叫spring事务传播级别?就是spring方法调用同级别方法、父级别方法、子级别方法的事务相互影响传递的过程。 spring事务类型枚举类Propagation.java。看下面事务列表特征,我们不难发现spring事务有共同特点:当父事务不被挂起,子事务的异常外抛都会影响父事务 。 声明:底下的各类测试是选择网络上别人的测试结果,并没有亲自测试。常用事务传播策略 REQUIRED、REQUIRES_NEW
事务传播类型 | 事务传播类型描述 | 特征 |
---|---|---|
REQUIRED | 如果事务管理器存在事务则加入事务,没有事务者新创建一个事务。spring默认事务传递级别 验证 | 父亲无事务:各子方法事务各自独立且不相互影响 |
父亲有事务:父、各子方法事务相互影响且异常回滚 | ||
SUPPORTS | 如果事务管理器没有事务,如果当前有事务,使用当前事务否则以非事务执行 验证 | 父亲无事务:各子方法无事务执行且不会滚 |
父亲有事务:父、各子方法事务相互影响且异常回滚 | ||
MANDATORY | 使用当前事务,如果当前事务管理器没有事务就抛出异常。验证 | 父亲无事务:抛出异常 |
父亲有事务:加入当前事务执行 | ||
REQUIRES_NEW | 如果当前事务管理器有事务,挂起当前事务,新开建一个事务。 验证 | 父亲无事务:不影响子事务执行 |
父亲有事务:父事务被挂起,等待子事务执行完毕后执行,且父子事务不相互影响 | ||
NOT_SUPPORTED | 以非事务方式执行,如果当前事务管理器有事务则挂起当前事务。 | 父亲无事务:以非事务方式执行 |
父亲有事务:父事务被挂起,然后子以非事务方式执行 | ||
NEVER | 以非事务方式执行,如果当前事务管理器有事务则抛出异常。 | 父亲无事务:非事务方式执行 |
父亲有事务:抛出异常 | ||
NESTED | 如果当前事务管理器有事务则嵌套事务内执行,如果没有事务则按照REQUIRED执行。 验证 | 父亲无事务:子事务独立不相互影响 |
父亲有事务:父子事务相互影响 |
2.2、当父事务不被挂起,子事务的异常外抛都会影响父事务原因分析
在整理事务传播级别测试时候发现,只要父事务不被挂起,子事务异常外抛都会影响父事务。原因还是aop切面的问题。在aop理解不管子事务、父事务都是一个独立的方法,区别只是父方法是否开启事务而已。如果父事务不被挂起,其就是受子事务影响的。
三、spring事务总结
spring事务需要了解事务的传播级别、spring事务加入如何实现、spring事务挂起如何实现、spring事务回滚提交如何实现。