事务介绍
事务:一组只能同时成功或失败的执行命令。
事务特性:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)
隔离级别:
- READ UNCOMMITTED (读未提交)SELECT语句以非锁定方式执行,但可能使用行的早期版本。因此,使用这个隔离级别,这样的读取是不一致的。这也叫脏读。
- READ COMMITTED(读已提交)即使在同一事务中,每个一致的读取都将设置并读取其自己的新快照。
- REPEATABLE READ (可重复读)这是InnoDB得默认隔离级别。 同一事务中的一致读取将读取第一次读取建立的 快照。这意味着,如果您SELECT 在“同一事务”中发出多个普通(非锁定)语句,则这些 SELECT语句彼此之间也是一致的。
- SERIALIZABLE (序列化)此级别类似于REPEATABLE READ,但如果禁用了自动提交,则InnoDB会将所有普通的SELECT语句隐式转换为SELECT ... FOR SHARE。 如果启用了自动提交,则SELECT是它自己的事务。 因此,它被认为是只读的,并且如果以一致的(非锁定)读取方式执行并且不需要阻塞其他事务就可以序列化。 (如果其他事务已修改选定的行,则强制普通SELECT阻止,请禁用自动提交。)
spring事务
spring事务是通过AOP代理启用此支持,并且事务建议由元数据(当前基于XML或基于注释)驱动。 AOP与事务性元数据(即被aop增强的事物)的组合产生了一个AOP代理,该代理将TransactionInterceptor(事务拦截器)与适当的TransactionManager(事务管理器)实现结合使用,以驱动方法调用周围的事务。
Spring的TransactionInterceptor为命令式和反应式编程模型提供事务管理。 拦截器通过检查方法返回类型来检测所需的事务管理风格。 返回反应性类型(例如Publisher或Kotlin Flow(或其子类型))的方法符合反应式事务管理的条件。 所有其他返回类型(包括void)都将代码路径用于命令式事务管理。
同时不同的事务管理风格需要不同的事务管理器,命令式事务需要PlatformTransactionManager,而反应式事务则使用ReactiveTransactionManager实现,TransactionManager在不同的环境中有不同的实现,如PlatformTransactionManager(纯jdbc)、JtaTransactionManager(JTA)、HibernateTransactionManager(Hibernate)等。
spring事务执行流程
TransactionDefinition接口指定:
-
传播行为:通常,事务范围内的所有代码都在该事务中运行。但是,如果在已存在事务上下文的情况下运行事务方法,则可以指定行为。例如,代码可以在现有事务中继续运行(常见情况),或者可以暂停现有事务并创建新事务。 Spring提供了EJB CMT熟悉的所有事务传播选项。要了解有关Spring中事务传播的语义的信息,请参阅事务传播。
-
隔离级别:此事务与其他事务的工作隔离的程度。例如,该交易能否看到其他交易的未提交写入?
-
超时回滚:超时之前该事务运行了多长时间,并被基础事务基础结构自动回滚。
-
只读状态:当代码读取但不修改数据时,可以使用只读事务。在某些情况下,例如使用Hibernate时,只读事务可能是有用的优化。
-
回滚规则:指定哪些异常(和可抛出对象)应引起自动回滚。
<tx:advice />标记指定的各种事务设置。 默认的<tx:advice />设置为:
- 传播行为 REQUIRED。
- 隔离级别 DEFAULT。
- 事务只读状态 false(读写)。
- 事务超时 默认为基础事务系统的默认超时,如果不支持超时,则默认为无。
- 回滚规则 任何RuntimeException都会触发回滚,而任何checkedException都不会触发。
Attribute | Required | Default | Description |
---|---|---|---|
name | Yes | 与事务属性关联的方法名称。 通配符(*)可用于将相同的事务属性设置与多种方法相关联(例如,get *,handle *,on * Event等)。 | |
propagation | No | REQUIRED | 事务传播行为。 |
isolation | No | DEFAULT | 事务隔离级别。 仅适用于传播设置REQUIRED或REQUIRES_NEW。 |
timeout | No | -1 | 事务超时(秒)。 仅适用于REQUIRED或REQUIRES_NEW传播。 |
read-only | No | false | 读写与只读事务。 仅适用于REQUIRED或REQUIRES_NEW。 |
rollback-for | No | 逗号分隔的触发回滚的Exception实例列表。 例如,com.foo.MyBusinessException,ServletException。 | |
no-rollback-for | No | 不触发回滚的Exception实例的逗号分隔列表。 例如,com.foo.MyBusinessException,ServletException。 |
事务传播行为(org.springframework.transaction.annotation.Propagation):
- REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED) 支持当前事务,如果不存在则创建新事务。*类似于同名的EJB事务属性。默认设置。
- SUPPORTS(TransactionDefinition.PROPAGATION_SUPPORTS) 支持当前事务,如果不存在,则以非事务方式执行。
- MANDATORY(TransactionDefinition.PROPAGATION_MANDATORY) 支持当前事务,如果不存在则抛出异常。
- REQUIRES_NEW(TransactionDefinition.PROPAGATION_REQUIRES_NEW) 创建新事务,如果当前事务存在,则挂起当前事务。尤其适用于JtaTransactionManager
- NOT_SUPPORTED(TransactionDefinition.PROPAGATION_NOT_SUPPORTED) 以非事务方式执行,如果存在当前事务,则挂起当前事务。尤其适用于JtaTransactionManager。
- NEVER(TransactionDefinition.PROPAGATION_NEVER) 以非事务方式执行,如果存在事务,则引发异常。
- NESTED(TransactionDefinition.PROPAGATION_NESTED) 如果当前事务存在,则在嵌套事务中执行,否则的行为类似于REQUIRED。
xml配置方式
基础工程 https://www.cnblogs.com/jinit/p/13912571.html
application.xml
<?xml version="1.0" encoding="GBK"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/tx
https://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.1.xsd">
<!--组件扫描-->
<context:component-scan base-package="tx.*"/>
<context:property-placeholder location="classpath:jdbc-config.properties" ignore-unresolvable="true"/>
<!--详细配置参数见 com.alibaba.druid.pool.DruidAbstractDataSource-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<property name="initialSize" value="1"/>
<property name="minIdle" value="1"/>
<property name="maxActive" value="20"/>
<property name="maxWait" value="60000"/>
<property name="timeBetweenEvictionRunsMillis" value="60000"/>
</bean>
<!--配置sqlSessionFactoryBean对象-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<!--mybatis核心配置文件-->
<property name="configLocation" value="classpath:mybatis.xml"/>
</bean>
<!--配置事务管理器-->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--配置aop通知-->
<tx:advice id="txAdvice" transaction-manager="txManager">
<!-- the transactional semantics... -->
<tx:attributes>
<tx:method name="get*" read-only="true"/>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<!--aop编程配置,配置切入点(即对该方法进行增强),任意类型返回值 tx包下,OrderService的所有方法-->
<!--<aop:config>
<aop:pointcut id="orderServiceOperation" expression="execution(* tx.OrderService.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="orderServiceOperation"/>
</aop:config>-->
<!--配置Mapper对象-->
<bean class="org.mybatis.spring.mapper.MapperFactoryBean" scope="singleton">
<property name="mapperInterface" value="tx.mapper.AccountMapper"/>
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
<bean class="org.mybatis.spring.mapper.MapperFactoryBean" scope="singleton">
<property name="mapperInterface" value="tx.mapper.OrderMapper"/>
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
<bean class="org.mybatis.spring.mapper.MapperFactoryBean" scope="singleton">
<property name="mapperInterface" value="tx.mapper.StorageMapper"/>
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
<!--注册OrderService bean-->
<bean id="orderService" class="tx.OrderService"/>
</beans>
OrderService.java
package tx;
import org.springframework.beans.factory.annotation.Autowired;
import tx.entity.*;
import tx.mapper.*;
/**
* @author :jty
* @date :20-10-30
*/
public class OrderService {
@Autowired
OrderMapper orderMapper;
@Autowired
StorageMapper storageMapper;
@Autowired
AccountMapper accountMapper;
//去掉注释使用xml
// @Transactional(rollbackFor = Exception.class)
public void doOrder(){
//bz0001库存减20
storageMapper.update(new Storage(null,"bz0001",100-20,100));
//增加20份订单
orderMapper.add(
new Order(null,0001,"bz0001",20,20*100));
//手动制造一个异常,观察数据库变化
int m=1/0;
accountMapper.update(new Account(null,0001,2000-20*100));
}
}
-
库存减少
storageMapper.update(new Storage(null,"bz0001",100-20,100));
-
订单增加
orderMapper.add(new Order(null,0001,"bz0001",20,20*100));
-
账户金额不变
accountMapper.update(new Account(null,0001,2000-20*100));
,被异常中断
-
将aop配置注释放开后观察到数据库不发生改变(重置数据库后执行代码)
<!--<aop:config>
<aop:pointcut id="orderServiceOperation" expression="execution(* tx.OrderService.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="orderServiceOperation"/>
</aop:config>-->
为不同的bean配置不同的事务
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
https://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 配置不同的切入点 -->
<aop:config>
<aop:pointcut id="defaultServiceOperation"
expression="execution(* x.y.service.*Service.*(..))"/>
<aop:pointcut id="noTxServiceOperation"
expression="execution(* x.y.service.ddl.DefaultDdlManager.*(..))"/>
<aop:advisor pointcut-ref="defaultServiceOperation" advice-ref="defaultTxAdvice"/>
<aop:advisor pointcut-ref="noTxServiceOperation" advice-ref="noTxAdvice"/>
</aop:config>
<!-- this bean will be transactional (see the 'defaultServiceOperation' pointcut) -->
<bean id="fooService" class="x.y.service.DefaultFooService"/>
<!-- this bean will also be transactional, but with totally different transactional settings -->
<bean id="anotherFooService" class="x.y.service.ddl.DefaultDdlManager"/>
<!-- 不同的通知 -->
<tx:advice id="defaultTxAdvice">
<tx:attributes>
<tx:method name="get*" read-only="true"/>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<tx:advice id="noTxAdvice">
<tx:attributes>
<tx:method name="*" propagation="NEVER"/>
</tx:attributes>
</tx:advice>
<!-- 其他配置同单个(上述) -->
</beans>
使用@Transactional
注释在上面的类级别使用,注释指示声明类(及其子类)的所有方法的默认值。 另外,每种方法都可以单独注释。 请注意,类级别的注释不适用于类层次结构中的祖先类。 在这种情况下,需要在本地重新声明方法,以参与子类级别的注释。
- 应仅将@Transactional注释应用于具有公共可见性的方法。如果使用注释对受保护的,私有的或程序包可见的方法进行@Transactional注释,则不会引发任何错误,但是带注释的方法不会显示已配置的事务设置。
- 建议使用@Transactional注释仅注释具体的类(以及具体类的方法),而不是注释接口。 您当然可以在接口(或接口方法)上放置@Transactional批注,但这仅在您使用基于接口的代理时才可以正常使用。 Java注释不是从接口继承的事实意味着,如果您使用基于类的代理(proxy-target-class="true")或基于编织的方面(mode="aspectj"),则事务设置不会 由代理和编织基础结构识别,并且该对象未包装在事务代理中。
application.xml
<?xml version="1.0" encoding="GBK"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/tx
https://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.1.xsd">
<!--组件扫描-->
<context:component-scan base-package="tx.*"/>
<context:property-placeholder location="classpath:jdbc-config.properties" ignore-unresolvable="true"/>
<!--详细配置参数见 com.alibaba.druid.pool.DruidAbstractDataSource-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<property name="initialSize" value="1"/>
<property name="minIdle" value="1"/>
<property name="maxActive" value="20"/>
<property name="maxWait" value="60000"/>
<property name="timeBetweenEvictionRunsMillis" value="60000"/>
</bean>
<!--配置sqlSessionFactoryBean对象-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<!--mybatis核心配置文件-->
<property name="configLocation" value="classpath:mybatis.xml"/>
</bean>
<!--配置事务管理器-->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- enable the configuration of transactional behavior based on annotations 开启事务注解-->
<!-- 去掉tx:advice和aop:config配置,通过@Transactional注解定义事务和定位aop代理目标-->
<tx:annotation-driven transaction-manager="txManager"/><!-- a TransactionManager is still required -->
<!--注释掉tx:advice和aop:config-->
<!--配置aop通知-->
<!--<tx:advice id="txAdvice" transaction-manager="txManager">
<!– the transactional semantics... –>
<tx:attributes>
<!–get方法事务只读–>
<tx:method name="get*" read-only="true" isolation="DEFAULT" timeout="-1" rollback-for="RuntimeException" propagation="REQUIRED" no-rollback-for="ArithmeticException"/>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>-->
<!--aop编程配置,配置切入点(即对该方法进行增强),任意类型返回值 tx包下,OrderService的所有方法-->
<!-- <aop:config>
<aop:pointcut id="orderServiceOperation" expression="execution(* tx.OrderService.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="orderServiceOperation"/>
</aop:config>-->
<!--配置Mapper对象-->
<bean class="org.mybatis.spring.mapper.MapperFactoryBean" scope="singleton">
<property name="mapperInterface" value="tx.mapper.AccountMapper"/>
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
<bean class="org.mybatis.spring.mapper.MapperFactoryBean" scope="singleton">
<property name="mapperInterface" value="tx.mapper.OrderMapper"/>
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
<bean class="org.mybatis.spring.mapper.MapperFactoryBean" scope="singleton">
<property name="mapperInterface" value="tx.mapper.StorageMapper"/>
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
<!--注册OrderService bean-->
<bean id="orderService" class="tx.OrderService"/>
</beans>
OrderService.java
public class OrderService {
@Autowired
OrderMapper orderMapper;
@Autowired
StorageMapper storageMapper;
@Autowired
AccountMapper accountMapper;
@Transactional(rollbackFor = Exception.class,propagation= Propagation.REQUIRED,isolation= Isolation.DEFAULT,timeout = -1)
public void doOrder(){
//bz0001库存减20
storageMapper.update(new Storage(null,"bz0001",100-20,100));
//增加20份订单
orderMapper.add(
new Order(null,0001,"bz0001",20,20*100));
//手动制造一个异常,观察数据库变化
int m=1/0;
accountMapper.update(new Account(null,0001,1500-20*100));
}
}
原理
<!--注册OrderService bean-->
<bean id="orderService" class="tx.OrderService"/>
<tx:advice id="txAdvice" transaction-manager="txManager">
<!-- the transactional semantics... -->
<tx:attributes>
<!--get方法事务只读-->
<tx:method name="get*" read-only="true" isolation="DEFAULT" timeout="-1" rollback-for="RuntimeException" propagation="REQUIRED" no-rollback-for="ArithmeticException"/>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="orderServiceOperation" expression="execution(* tx.OrderService.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="orderServiceOperation"/>
</aop:config>
- 通过tx:advice标签可知道该标签配置的对象为
org.springframework.transaction.interceptor.TransactionInterceptor
- 通过aop:config标签可知道spring通过txAdvice对象的invoke(MethodInvocation invocation)方法代理orderServiceOperation对象
- 即通过该方法
org.springframework.transaction.interceptor.TransactionInterceptor#invoke
代理我们定义的切入点
//方法调用的描述,在方法调用时提供给拦截器。即在调用切入掉表达式式定义的方式时,将被拦截获得该对象MethodInvocation
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...
//invocation.getMethod():tx.OrderService.doOrder(),被代理的方法;invocation::proceed:invocation的proceed方法作为参数,
//‘::’方法引用,接受该方法的接口
/*@FunctionalInterface
protected interface InvocationCallback {
Object proceedWithInvocation() throws Throwable;
}*/
//即InvocationCallback.proceedWithInvocation()相当于invocation.proceed();
//旧版本代码如下
/* return invokeWithinTransaction(invocation.getMethod(), targetClass, new InvocationCallback() {
@Override
public Object proceedWithInvocation() throws Throwable {
return invocation.proceed();
}
});*/
return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
}
TransactionAspectSupport.invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,final InvocationCallback invocation)
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
final InvocationCallback invocation) throws Throwable {
// If the transaction attribute is null, the method is non-transactional.
//获取 <tx:attributes>中的配置
TransactionAttributeSource tas = getTransactionAttributeSource();
//获取事务定义参数
final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
//确定要用于给定事务的特定事务管理器。
final TransactionManager tm = determineTransactionManager(txAttr);
//反应式事务
if (this.reactiveAdapterRegistry != null && tm instanceof ReactiveTransactionManager) {
ReactiveTransactionSupport txSupport = this.transactionSupportCache.computeIfAbsent(method, key -> {
if (KotlinDetector.isKotlinType(method.getDeclaringClass()) && KotlinDelegate.isSuspend(method)) {
throw new TransactionUsageException(
"Unsupported annotated transaction on suspending function detected: " + method +
". Use TransactionalOperator.transactional extensions instead.");
}
ReactiveAdapter adapter = this.reactiveAdapterRegistry.getAdapter(method.getReturnType());
if (adapter == null) {
throw new IllegalStateException("Cannot apply reactive transaction to non-reactive return type: " +
method.getReturnType());
}
return new ReactiveTransactionSupport(adapter);
});
return txSupport.invokeWithinTransaction(
method, targetClass, invocation, txAttr, (ReactiveTransactionManager) tm);
}
//非反应式事务
PlatformTransactionManager ptm = asPlatformTransactionManager(tm);
//获取连接点 tx.OrderService.doOrder
final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {
// Standard transaction demarcation with getTransaction and commit/rollback calls.
//使用getTransaction和commit / rollback调用进行标准事务划分。
//获取事务信息 包括事务管理器、连接点、事务定义参数、事务状态等
TransactionInfo txInfo = createTransactionIfNecessary(ptm, 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);
}
if (vavrPresent && VavrDelegate.isVavrTry(retVal)) {
// Set rollback-only in case of Vavr failure matching our rollback rules...
TransactionStatus status = txInfo.getTransactionStatus();
if (status != null && txAttr != null) {
retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status);
}
}
//若没异常发生则在该处提交事务
commitTransactionAfterReturning(txInfo);
return retVal;
}
else {
final ThrowableHolder throwableHolder = new ThrowableHolder();
// It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
try {
Object result = ((CallbackPreferringPlatformTransactionManager) ptm).execute(txAttr, status -> {
TransactionInfo txInfo = prepareTransactionInfo(ptm, txAttr, joinpointIdentification, status);
try {
Object retVal = invocation.proceedWithInvocation();
if (vavrPresent && VavrDelegate.isVavrTry(retVal)) {
// Set rollback-only in case of Vavr failure matching our rollback rules...
retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status);
}
return retVal;
}
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;
}
}
}