文章目录
1. @Transactional的使用
- 添加在接口实现类或接口实现方法上,而不是接口类中。
- 访问权限:private的方法事务不起作用。因为事务使用的是
cglib
代理,cglib
代理底层是基于⽗⼦类来实现的, ⼦类是不能重载⽗类的private
⽅法的,所以⽆法很好的利⽤代理,也会导致@Transactianal失效 - 接口中A、B两个方法,A无@Transactional标签,B有,通过A调用B,此时B事务生效,A方法因为无@Transactional标签,所以事务不生效。如果A方法在B方法 执行完毕后抛了异常,则整个流程相当于无事务!
- 同一个类中,有两个方法 A、B,如下所示,无论被调用的b()方法是否配置了事务,此事务在被调用时都将不生效。B方法抛异常后也不会回滚!
原因是:spring在扫描bean的时候会扫描方法上是否包含@Transaction注解,如果包含,spring会为这个bean动态生成一个代理类,代理类中增加了事务功能。此时,当这个有注解的方法被调用的时候,实际上是由代理类来调用的,而在本例中,类内部a()调用b()的过程中,实质执行的代码是this.b(),此处this对象是实际的serviceImpl对象而不是本该生成的代理对象,因此直接调用了b()方法。事务是不会生效的! 这和@Async异步在同一个类中调用,异步失效的原理类似!
@Service
public class UserServiceImpl implements UserService {
/**
因为Spring事务是基于代理来实现的,
所以某个加了 @Transactional 的⽅法只有被代理对象调⽤时,那么这个注解才会⽣效,
否则 @Transactional 是不会⽣效的。
*/
//普通方法A
public int A(User user) {
//插入操作
userDao.insertUser(user);
// 调用本类中的事务方法B
this.B(user.getId());
return 1;
}
//事务方法B
@Transactional
public String B(int id) {
//修改操作
//新增操作
}
}
- 默认配置下,spring 只有在抛出的异常为RuntimeException 时才回滚该事务。
- 多线程下事务管理因为线程不属于 spring 托管,故多线程不能够默认使用 spring 的事务。
由于配置了@Transactional就需要AOP拦截及事务的处理,可能影响系统性能。
①:方法内调用,事务失效解决方案
上边说到方法内调用事务方法,会导致事务失效,@Async也一样。原因就在于:调用时用的this当前对象,没有使用代理对象,当然也就没有事务、异步等逻辑处理了,对此有三种解决方案
-
把该事务、异步方法放在别的
@Service
类中,然后在当前方法中注入该@Service
类,然后再调用该@Service类中的事务方法! -
使用编程式事务
@Override public void placeOrder() { // 此处省略一堆逻辑 // TransactionCallbackWithoutResult 无返回参数 // TransactionCallback 有返回参数 transactionTemplate.execute(new TransactionCallbackWithoutResult() { @Override protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) { try { this.updateByTransactional(); } catch (Exception e) { log.error("下单失败", e); transactionStatus.setRollbackOnly(); } } }); } public void updateByTransactional() { // 修用户改余额和商品库存 accountMapper.update(); productMapper.update(); }
-
先从容器中获取到该代理类,然后用代理类调用事务方法
// hutool工具类获取当前代理类 ShopeeReceivableNotesService proxy = SpringUtil.getBean(this.getClass()); // 使用代理类调用事务方法,注意该方法也要在接口中存在! proxy.removeAndSaveReceivable(beginTime, endTime, shopId, entityList);
2. spring事务的原理
Spring的事务和Spring的Aop,他们之间的套路非常相似,都是由以下三步组成:
- ①:Spring boot 项目启动,进行事务自动注册时,通过
@Import
注册一个创建动态代理的bean的后置处理器 和BeanFactoryTransactionAttributeSourceAdvisor
、TransactionAttributeSource
、TransactionInterceptor
等处理事务相关的bean - ②:在创建
bean
时,通过bean后置处理器的postProcessBeforeInstantistion
方法找到@Trantional
注解标注的方法,并存入缓存,这点与AOP逻辑类似 - ③:创建动态代理,在bean后置处理器的
postProcessAfterInstantistion
方法中与当前正在创建的bean进行匹配,如果bean中的方法或者类上有@Trantional
注解,则为其创建动态代理! - ③:当调用带有
@Trantional
注解的方法或类时, 会进入TransactionInterceptor
中,触发代理逻辑。会先开启事务,然后分别处理嵌套事务和单一事务,以及两种事务的各种传播行为!如果抛异常则回滚方法,无异常则提交事务!
下面就一一介绍这三步操作,在Spring事务中的实现。
2.1 开启事务,注册bean的后置处理器和相关bean对象,封装Advisor
从配置类查看事务源码,配置类如下:
@EnableTransactionManagement //开启事务
@EnableAspectJAutoProxy(exposeProxy = true) //开启Aop
@ComponentScan(basePackages = {"com.**"}) //包扫描
public class MainConfig {
//使用@EnableTransactionManagement注解前,
//请务必保证你已经配置了至少一个PlatformTransactionManager的Bean,否则会报错。
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
由@EnableTransactionManagement
进入源码,通过@Import
注解帮我们导入了事务组件TransactionManagementConfigurationSelector
,在容器初始化的时候注册。
/**
* @Import注解可以为我们容器中导入组件--->TransactionManagementConfigurationSelector
*/
@Import(TransactionManagementConfigurationSelector.class)
@Import
导入的组件会在refresh()
方法中的invokeBeanFactoryPostProcessors
中注册,如下:
// 调用我们的bean工厂的后置处理器.
invokeBeanFactoryPostProcessors(beanFactory);
TransactionManagementConfigurationSelector
代码如下:
public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {
/**
* 在springioc容器中加载bean定义的时候会回调我们的selectImports方法
* 方法的返回值是我们需要导入类的全类名路径.然后这个类就会被加载到容器中
*/
@Override
protected String[] selectImports(AdviceMode adviceMode) {
switch (adviceMode) {
/**
* PROXY:jdk代理默认使用这个
* 为我们的容器中导入了二个组件 一个是AutoProxyRegistrar
* 一个是ProxyTransactionManagementConfiguration
*/
case PROXY:
return new String[] {AutoProxyRegistrar.class.getName(),
ProxyTransactionManagementConfiguration.class.getName()};
// ASPECTJ:这个需要使用AspectJ的编译器,麻烦,一般不使用
case ASPECTJ:
return new String[] {
TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME};
default:
return null;
}
}
}
在TransactionManagementConfigurationSelector
组件中,默认case的是PROXY,它给我们容器注册了两个组件,一个是AutoProxyRegistrar,另一个是ProxyTransactionManagementConfiguration,这两个组件非常重要,下面一一来介绍:
- ①:AutoProxyRegistrar:作用就是向Spring容器注入了一个创建动态代理的bean的后置处理器(这个事务的bean的后置处理器只针对内部的类起作用),我们已经知道,Aop也会帮我们创建动态代理的bean的后置处理器。那么问题来了,如果一个系统同时用到了事务和Aop,这样是不是就会注册两个bean的后置处理器?创建两套动态代理? 答案肯定是不会的!带着这个问题,我们看一下事务的源码是怎么处理的。
类图如下:
//事务调用这个方法注册bean的后置处理器(由AutoProxyRegistrar进入)
@Nullable
public static BeanDefinition registerAutoProxyCreatorIfNecessary(
BeanDefinitionRegistry registry, @Nullable Object source) {
return registerOrEscalateApcAsRequired(InfrastructureAdvisorAutoProxyCreator.class, registry, source);
}
//Aop调用这个方法注册bean的后置处理器
@Nullable
public static BeanDefinition registerAspectJAutoProxyCreatorIfNecessary(
BeanDefinitionRegistry registry, @Nullable Object source) {
return registerOrEscalateApcAsRequired(AspectJAwareAdvisorAutoProxyCreator.class, registry, source);
}
可以看到,两个方法都调用了registerOrEscalateApcAsRequired
方法,进去一探究竟,如何避免事务和Aop同时存在从而对一个bean创建多个动态代理呢?答案就是优先级覆盖,事务的优先级低于Aop,如果同时存在,Aop会覆盖事务注册的bean的后置处理器
private static BeanDefinition registerOrEscalateApcAsRequired(
Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
//因为事务和Aop都是一样的名字,所以内部会根据优先级覆盖beanClass
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
//当同时有事务和Aop,两者都会创建bean的后置处理器
//为了防止创建两个,在内部进行了优先级覆盖
//事务优先级为 1, Aop的优先级为 3
int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
int requiredPriority = findPriorityForClass(cls);
//比较优先级,此时Aop会覆盖事务
if (currentPriority < requiredPriority) {
apcDefinition.setBeanClassName(cls.getName());
}
}
return null;
}
RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
beanDefinition.setSource(source);
beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
return beanDefinition;
}
- ②:ProxyTransactionManagementConfiguration
它是一个@Configuration
,给容器中注册了三个bean对象
-
BeanFactoryTransactionAttributeSourceAdvisor
:事务的Advisor,封装了事务的切面信息,与Aop不同的是,Aop需要自定义切面逻辑,然后调用AspectJ来解析切面封装成Advisor。而事务不同,Spring内部帮我们封装好事务的逻辑,连Advisor在这里都直接帮我们设置好了@Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME) @Role(BeanDefinition.ROLE_INFRASTRUCTURE) public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor() { BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor(); //设置事务的事务属性源AttributeSource,解析@Transactional注解,类似于切点 advisor.setTransactionAttributeSource(transactionAttributeSource()); //设置事务的通知逻辑 advisor.setAdvice(transactionInterceptor()); if (this.enableTx != null) { advisor.setOrder(this.enableTx.<Integer>getNumber("order")); } return advisor; }
-
TransactionAttributeSource
:获取事务属性对象,解析@Transactional
注解。只有带有@Transactional
的类才会被创建动态代理@Bean @Role(BeanDefinition.ROLE_INFRASTRUCTURE) public TransactionAttributeSource transactionAttributeSource() { return new AnnotationTransactionAttributeSource(); }
-
TransactionInterceptor
:里边有Spring事务的逻辑,拦截事务方法,执行事务逻辑!@Bean @Role(BeanDefinition.ROLE_INFRASTRUCTURE) public TransactionInterceptor transactionInterceptor() { TransactionInterceptor interceptor = new TransactionInterceptor(); interceptor.setTransactionAttributeSource(transactionAttributeSource()); if (this.txManager != null) { interceptor.setTransactionManager(this.txManager); } return interceptor; }
2.2 匹配并创建动态代理
因为事务和Aop不一样,Aop根据不同的切点和Advice会有多个Advisor,而事务只有一个Advisor。也就是上一步开启事务时,已经放入容器的BeanFactoryTransactionAttributeSourceAdvisor类。接下来只需要拿当前创建的bean与事务的Advisor匹配,匹配成功则创建动态代理即可。这个过程与Aop达成了代码复用。
如何判断当前类具备事务,具有创建动态代理的资格?
答:从当前正在初始化的bean的所有的方法中,找到带有@Transactional的方法,根据方法优先原则,由 本类方法 ==> 接口方法 ==> 父类方法的顺序去找,如果找到,就表示有创建动态代理的资格。如果方法上都没有,则去类上面找,由 本类上 ==> 接口上 ==> 父类上 的顺序去找,如果找到,就表示有创建动态代理的资格。
如果具备创建动态代理的资格,就会为当前bean创建动态代理,这个过程与Aop一样,在此不多说,详情请看 Spring的Aop源码逻辑
2.3 调用事务的代理逻辑
如果启动事务,在容器启动时,Spring会在容器中注入TransactionInterceptor这个bean对象。这个bean里边有Spring事务的逻辑,作用是拦截事务方法,执行事务逻辑!
当为某个类创建了事务的动态代理后,就会执行TransactionInterceptor中处理Spring事务的逻辑。具体代码如下:
@Nullable
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
final InvocationCallback invocation) throws Throwable {
//获取 事务属性源
TransactionAttributeSource tas = getTransactionAttributeSource();
//通过 事务属性源 获取 事务属性信息
final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
//获取我们配置的事务管理器
final PlatformTransactionManager tm = determineTransactionManager(txAttr);
//从txAttr事务属性对象 中获取出标注了@Transactionl的方法名
final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
//处理声明式事务
if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
//如果有必要,则创建事务(开启事务)
TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
Object retVal;
try {
//调用钩子函数进行回调目标方法,事务也会责任链调用,不过责任链中只有一个
retVal = invocation.proceedWithInvocation();
}
catch (Throwable ex) {
//抛出异常进行回滚处理
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
//清空我们的线程变量中transactionInfo的值
cleanupTransactionInfo(txInfo);
}
//提交事务
commitTransactionAfterReturning(txInfo);
return retVal;
}
//处理编程式事务
.......
}
其中最主要的就是createTransactionIfNecessary这个方法,它内部封装了开启事务的操作,单事务和嵌套事务分别有不同的操作,大致流程如下图
Spring针对不同的传播行为有不同的处理,下面来了解一下事务的传播行为
事务的传播行为
事务传播行为 | 传播行为描述 | 使用方式 |
---|---|---|
REQUIRED(默认) | 如果当前存在事务,则加入该事务, 如果当前不存在事务,则创建一个新的事务。 | @Transactional(propagation = Propagation.REQUIRED)适用增删改查 |
SUPPORTS | 如果当前存在事务,则加入该事务; 如果当前不存在事务,则以非事务的方式继续运行。 | @Transactional(propagation = Propagation.SUPPORTS)适用查询 |
REQUIRES_NEW | 重新创建一个新的事务, 如果当前存在事务,延缓当前的事务。 两个事务的提交回滚互不干扰 | @Transactional(propagation = Propagation.REQUIRES_NEW)适用内部事务和外部事务不存在业务关联情况,如日志 |
NOT_SUPPORTED | 以非事务的方式运行,如果当前存在事务,暂停当前的事务。 | @Transactional(propagation = Propagation.NOT_SUPPORTED)不常用 |
NEVER | 以非事务的方式运行, 如果当前存在事务,则抛出异常。 | @Transactional(propagation = Propagation.NEVER )不常用 |
MANDATORY | 如果当前存在事务,则加入该事务; 如果当前不存在事务,则抛出异常。 | @Transactional(propagation = Propagation.MANDATORY)不常用 |
NESTED | 如果没有,就新建一个事务; 如果有,就在当前事务中嵌套其他事务。,外层影响内层, 内层不会影响外层 | @Transactional(propagation = Propagation.NESTED)不常用 |
PROPAGATION_REQUIRED
:- 如果当前正要执行的事务不在另外一个事务里,那么就起一个新的事务。比如说,
ServiceB
.methodB
的事务级别定义为PROPAGATION_REQUIRED
, 那么由于执行ServiceA.methodA的
时候,ServiceA.methodA
已经起了事务,这时调用ServiceB.methodB
,ServiceB.methodB
看到自己已经运行在ServiceA.methodA
的事务内部,就不再起新的事务。 - 而假如
ServiceA.methodA
运行的时候发现自己没有在事务中,他就会为自己分配一个事务。 这样,在ServiceA.methodA
或者在ServiceB.methodB
内的任何地方出现异常,事务都会被回滚。即使ServiceB.methodB
的事务已经被 提交,但是ServiceA.methodA
在接下来fail要回滚,ServiceB.methodB
也要回滚
- 如果当前正要执行的事务不在另外一个事务里,那么就起一个新的事务。比如说,
PROPAGATION_SUPPORTS
:- 如果当前在事务中,即以事务的形式运行,如果当前不再一个事务中,那么就以非事务的形式运行。这就跟平常用的普通非事务的代码只有一点点区别了。不理这个,因为我也没有觉得有什么区别
PROPAGATION_REQUIRES_NEW
:- 假如设计
ServiceA.methodA
的事务级别为PROPAGATION_REQUIRED
,ServiceB.methodB
的事务级别为PROPAGATION_REQUIRES_NEW
, 那么当执行到ServiceB.methodB
的时候,ServiceA.methodA
所在的事务就会挂起,ServiceB.methodB
会起一个新的事务,等待ServiceB.methodB
的事务完成以后,
他才继续执行。 - 他与
PROPAGATION_REQUIRED
的事务区别在于事务的回滚程度了。因为ServiceB.methodB
是新起一个事务,那么就是存在两个不同的事务。如果ServiceB.methodB
已经提交,那么ServiceA.methodA
失败回滚,ServiceB.methodB
是不会回滚的。如果ServiceB.methodB
失败回滚,如果他抛出的异常被ServiceA.methodA
捕获,ServiceA.methodA
事务仍然可能提交。
- 假如设计
PROPAGATION_NOT_SUPPORTED
:- 当前不支持事务。比如
ServiceA.methodA
的事务级别是PROPAGATION_REQUIRED
,而ServiceB.methodB
的事务级别是PROPAGATION_NOT_SUPPORTED
- 那么当执行到
ServiceB.methodB
时,ServiceA.methodA
的事务挂起,而他以非事务的状态运行完,再继续ServiceA.methodA
的事务。
- 当前不支持事务。比如
PROPAGATION_NEVER
:- 不能在事务中运行。假设
ServiceA.methodA
的事务级别是PROPAGATION_REQUIRED
, 而ServiceB.methodB
的事务级别是PROPAGATION_NEVER
, 那么ServiceB.methodB
就要抛出异常了。
- 不能在事务中运行。假设
PROPAGATION_MANDATORY
:- 必须在一个事务中运行。也就是说,他只能被一个父事务调用。否则,他就要抛出异常。
PROPAGATION_NESTED
:- 他与
PROPAGATION_REQUIRES_NEW
的区别是,PROPAGATION_REQUIRES_NEW
另起一个事务,将会与他的父事务相互独立,而PROPAGATION_NESTED
则是外部事务的子事务。也就是说,如果父事务最后回滚,他也要回滚的,父事务最后commit
,他也跟着commit
。 - 如果子事务
ServiceB.methodB
失败回滚,那么ServiceA.methodA
也会回滚到savepoint
点上,ServiceA.methodA
可以选择另外一个分支,比如ServiceC.methodC
,继续执行,来尝试完成自己的事务。
- 他与
接下来继续源码,看一下单事务与嵌套事务的区别!
单事务逻辑比较简单,如果是嵌套事务,比如以下操作
@Transactional(rollbackFor = Exception.class) //事务方法
public void pay(String accountId, double money) {
//查询余额
double blance = accountInfoDao.qryBlanceByUserId(accountId);
//嵌套事务,更新库存
this.updateProductStore(1);
}
@Transactional(propagation = Propagation.REQUIRES_NEW) //事务方法
public void updateProductStore(Integer productId) {
。。。。。。省略代码
}
下面来讲一下嵌套事务的流程,以上面伪代码为例,pay方法内部调用了updateProductStore
方法,会开启一个新事务,首先pay方法会开启一个事务,获取一个数据库连接Connection,封装到ConnectionHolder
,开启事务,绑定数据源和ConnectionHolder
到事务管理器TransactionSynchronizationManager
上!逻辑封装在doBegin
方法中:
protected void doBegin(Object transaction, TransactionDefinition definition) {
//强制转化事物对象
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
Connection con = null;
try {
//判断事务对象没有数据库连接持有器
if (!txObject.hasConnectionHolder() ||
txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
//通过数据源获取一个数据库连接对象,这个链接是我们自己设置的
Connection newCon = obtainDataSource().getConnection();
if (logger.isDebugEnabled()) {
logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");
}
//把我们的数据库连接包装成一个ConnectionHolder对象 然后设置到我们的txObject对象中去
txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
}
//标记当前的连接是一个同步事务
txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
con = txObject.getConnectionHolder().getConnection();
//为当前的事务设置隔离级别
Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
txObject.setPreviousIsolationLevel(previousIsolationLevel);
//关闭自动提交
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);
}
// 绑定我们的数据源和连接到我们的同步管理器上 把数据源作为key,数据库连接作为value 设置到线程变量中
if (txObject.isNewConnectionHolder()) {
TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());
}
}
然后再次补充事务管理器信息,把当前事务的信息绑定到事务管理器TransactionSynchronizationManager
上
protected void prepareSynchronization(DefaultTransactionStatus status, TransactionDefinition definition) {
if (status.isNewSynchronization()) {
//绑定事务激活
TransactionSynchronizationManager.setActualTransactionActive(status.hasTransaction());
//当前事务的隔离级别
TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(
definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT ?
definition.getIsolationLevel() : null);
//是否为只读事务
TransactionSynchronizationManager.setCurrentTransactionReadOnly(definition.isReadOnly());
//事务的名称
TransactionSynchronizationManager.setCurrentTransactionName(definition.getName());
TransactionSynchronizationManager.initSynchronization();
}
}
然后回调目标方法,启动责任链调用,链上有 pay 和updateProductStore两个节点,此时进入pay节点
//调用钩子函数进行回调目标方法,事务也会责任链调用
retVal = invocation.proceedWithInvocation();
因为pay
方法内部还有updateProductStore
事务方法,所以pay
方法执行一半会暂停,通过递归的方式进入updateProductStore
方法,updateProductStore
方法属于内部事务,会走嵌套事务逻辑。此时内部事务(updateProductStore
)会挂起外部事务(pay
),把外部事务(pay
)的属性从TransactionSynchronizationManager
中取出来,再封装到SuspendedResourcesHolder
做一个临时存储。
然后在开启事务时把内部事务(updateProductStore
)的属性填充到已经为空的事务管理器TransactionSynchronizationManager
中。等内部事务方法(updateProductStore
)处理完毕并提交后,再把SuspendedResourcesHolder中临时存储的外部事务pay的事务信息取出,放入事务管理器TransactionSynchronizationManager
中,覆盖掉。再执行外部方法pay
,如果抛出异常,会进行回滚,最后清空我们的线程变量中transactionInfo
的值,提交事务。这才是嵌套事务的整个流程
注意: 异常回滚和内部事务提交都会清空事务同步管理器TransactionSynchronizationManager
的信息,意义在于:内部事务执行完毕,开始执行外部事务!
3. @PostConstruct方法中使用@Transactional注解,事务无效
在@PostConstruct
注解上加@Transactional
,事务是不生效的!(与InitializingBean
接口中的afterPropertiesSet
也是如此!无法确保已完成所有后期处理,因此(实际上)不能有任何事务。确保工作正常的唯一方法是使用TransactionTemplate
。
@Service("something")
public class Something {
@Autowired
@Qualifier("transactionManager")
protected PlatformTransactionManager txManager;
@PostConstruct
private void init(){
TransactionTemplate tmpl = new TransactionTemplate(txManager);
tmpl.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
//PUT YOUR CALL TO SERVICE HERE
}
});
}
}
案例如下:
@Autowired
@Qualifier("transactionManager")
protected PlatformTransactionManager txManager;
@PostConstruct
public void updaeAll() {
TransactionTemplate tmpl = new TransactionTemplate(txManager);
tmpl.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
List<OrderEntity> list = orderService.list();
list.forEach(x -> {
x.setAmount(6);
//多个更新操作
orderService.saveOrUpdate(x);
//在第五个操作时,抛出异常!
if (x.getId() == 5) {
int i = 1 / 0;
}
});
}
});
}
上述代码运行,事务会回滚,如果不使用TransactionTemplate
,而单纯使用@Transactional注解
,事务不会生效!