Transactional 原理

@Transactional 是 Spring 框架中用于实现事务管理的注解。事务是指一系列数据库操作,它们要么全部成功执行,要么全部失败回滚。使用 @Transactional 注解可以确保一组数据库操作要么全部成功提交,要么全部失败回滚,保持数据的一致性和完整性

事务Transaction

数据库事务是指一组数据库操作,这些操作被视为一个单独的工作单元,并且要么全部成功执行,要么全部失败回滚,以保持数据库的一致性和完整性

事务具有四个特性,通常称为 ACID 特性:

  1. 原子性(Atomicity:事务是一个不可分割的操作单元,要么全部执行成功,要么全部失败回滚。如果其中任何一个操作失败,整个事务将回滚到最初状态,不会留下部分完成的结果

  2. 一致性(Consistency:事务在执行前和执行后,数据库的状态必须保持一致。这意味着事务在执行后,数据库从一个一致的状态转换到另一个一致的状态

  3. 隔离性(Isolation:事务之间是相互隔离的,一个事务的执行不应该受到其他事务的干扰。即使多个事务同时执行,它们也不能相互干扰,每个事务都应该感觉自己在独立执行

  4. 持久性(Durability:一旦事务成功提交,其结果将被永久保存在数据库中,即使发生系统故障,数据也不会丢失

数据库事务的常用操作有:

  1. 开始事务(BEGIN 或 START TRANSACTION):开始一个新的事务,此时数据库会记录当前的状态

  2. 提交事务(COMMIT):如果事务中所有的操作都成功执行,那么就提交事务,将结果保存到数据库中

  3. 回滚事务(ROLLBACK):如果事务中的任何一个操作失败,回滚事务,将数据库状态恢复到事务开始之前的状态

事务的应用场景包括金融交易、订单处理、库存管理等需要确保数据完整性和一致性的场景。在数据库事务中,要注意设计良好的事务边界,避免事务持续时间过长,以免影响数据库的性能和并发处理能力

使用事务能够确保数据库操作的可靠性,是保障数据完整性和可靠性的重要手段。数据库管理系统(DBMS)负责实现和管理事务,并根据数据库的特性和事务的要求来确保 ACID 特性的实现

@Transactional

Spring中的 @Transactional 基于动态代理的机制,提供了一种透明的事务管理机制,方便快捷解决在开发中碰到的问题。在现实中,实际的问题往往比我们预期的要复杂很多,这就要求对 @Transactional 有深入的了解,以来应对复杂问题

在 Spring 中,@Transactional 注解可以用于类或方法级别。当应用在类级别上时,表示该类的所有公共方法都将受到事务管理。而当应用在方法级别上时,只有被注解的方法才会受到事务管理

要使用 @Transactional 注解,需要满足以下条件:

  1. 在 Spring 配置文件中启用事务管理器或者在启动类上添加 @EnableTransactionManagement

  2. 在需要事务管理的类上或方法上添加 @Transactional 注解

在Spring-boot里会自动配置事务,一般不需要手动开启事务管理

相关的配置在 org.boot.autoconfigure.transaction.TransactionAutoConfiguration

示例代码如下:

  1. 启用事务管理器:
1
2
3
4
5
6
7
8
9
10
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">

<tx:annotation-driven />
<!-- 配置数据源和其他相关信息 -->
</beans>
  1. 在类或方法上添加 @Transactional 注解:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class MyService {

@Transactional
public void doSomethingInTransaction() {
// 执行数据库操作,这些操作会在事务管理下进行
}

@Transactional(rollbackFor = Exception.class)
public void doSomethingWithRollback() throws Exception {
// 执行数据库操作,如果发生异常,事务会回滚
}
}

在上面的示例中,doSomethingInTransactiondoSomethingWithRollback 方法都被 @Transactional 注解标记,这意味着它们会受到事务管理。如果在 doSomethingWithRollback 方法中发生了异常,事务将回滚,数据库操作将被撤销,保持数据的一致性

@EnableTransactionManagement

1
2
3
4
5
6
7
8
9
10
11
12
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({TransactionManagementConfigurationSelector.class})
public @interface EnableTransactionManagement {
// 指定代理对象生成方式,true为cglib,否则为jdk
boolean proxyTargetClass() default false;
// 指定拦截器的生成模式,默认为代理模式
AdviceMode mode() default AdviceMode.PROXY;
// 事务拦截器优先级
int order() default Integer.MAX_VALUE;
}

@EnableAspectJAutoProxy 注解类似,都是向Spring导入了一个 TransactionManagementConfigurationSelector 组件,它是属于 ImportSelector 类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {

protected String[] selectImports(AdviceMode adviceMode) {
// 从注解中获得的AdviceMode,默认为PROXY
switch (adviceMode) {
// 向Spring容器添加了两个bean,分别为:
// AutoProxyRegistrar 和 ProxyTransactionManagementConfiguration
case PROXY:
return new String[]{AutoProxyRegistrar.class.getName(), ProxyTransactionManagementConfiguration.class.getName()};
case ASPECTJ:
return new String[]{this.determineTransactionAspectClass()};
default:
return null;
}
}
}
AutoProxyRegistrar

image-20230725134053856

根据类的继承图可知,它属于 ImportBeanDefinitionRegistrar 类型,Spring最终会调用内部的registerBeanDefinitions 方法,下述是源码中比较关键的代码

1
2
3
4
5
6
7
if (mode == AdviceMode.PROXY) {
AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
if ((Boolean) proxyTargetClass) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
return;
}
}

在使用 @EnableTransactionManagement 注解时,并没有特意去修改内部方法的返回值。因此,这里的mode就是AdviceMode.PROXY,proxyTargetClass为false。由源码逻辑可知:Spring最终会执行这段代码:AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry) 这段代码的主要功能就是向Spring容器中添加这个bean:InfrastructureAdvisorAutoProxyCreator.class

image-20230725141731103

与之前 AOP 中的 @EnableAspectJAutoProxy 内部导入的 AnnotationAwareAspectJAutoProxyCreator bean的类继承图十分相似,都继承了 AbstractAdvisorAutoProxyCreator

整个Spring AOP寻找切面、切面、通知的过程就是此方法 InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation 的功劳,而生成AOP代理对象就是 BeanPostProcessor#postProcessAfterInitialization 的功劳。而这些寻找切面、生成代理对象的功能其实是抽象父类 AbstractAutoProxyCreator 的功能。因此,InfrastructureAdvisorAutoProxyCreatorv也具备了寻找切面、切面、通知以及生成代理对象的功能

ProxyTransactionManagementConfiguration

image-20230725142415730

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {
public ProxyTransactionManagementConfiguration() {
}

@Bean(
name = {"org.springframework.transaction.config.internalTransactionAdvisor"}
)
@Role(2)
public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor(TransactionAttributeSource transactionAttributeSource, TransactionInterceptor transactionInterceptor) {
BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
// 内部维护了事务相关的属性源
advisor.setTransactionAttributeSource(transactionAttributeSource);
// 内部维护了执行事务时的拦截器,后续会依赖这个拦截器来开启、提交/回滚事务
// 当调用拥有事务的方法时,最终会调用到此拦截器内部的invoke方法
advisor.setAdvice(transactionInterceptor);
if (this.enableTx != null) {
advisor.setOrder((Integer)this.enableTx.getNumber("order"));
}

return advisor;
}

@Bean
@Role(2)
public TransactionAttributeSource transactionAttributeSource() {
return new AnnotationTransactionAttributeSource();
}

@Bean
@Role(2)
public TransactionInterceptor transactionInterceptor(TransactionAttributeSource transactionAttributeSource) {
TransactionInterceptor interceptor = new TransactionInterceptor();
interceptor.setTransactionAttributeSource(transactionAttributeSource);
if (this.txManager != null) {
interceptor.setTransactionManager(this.txManager);
}

return interceptor;
}
}

这个类主要功能:

  • 构建事务拦截器 transactionInterceptor
  • 构建事务属性数据源 transactionAttributeSource
  • 构建 Advisor 类型的 bean transactionAdvisor

事务实现

编程式事务

所谓编程式事务就是手动在代码中完成事务的提交,发生异常时的回滚。

在实现类中注入PlatformTransactionManager

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Autowired
private PlatformTransactionManager transactionManager;

@Override
public Map<String, Object> saveResource(MultipartFile file) {

TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
try {
// 相关业务

// 手动提交
transactionManager.commit(status);
} catch (Exception e) {
log.error("Exception:{}", ExceptionUtil.stacktraceToString(e));
// 发生异常时进行回滚
transactionManager.rollback(status);
}
}
声明式事务

所谓声明式事务,就是使用@Transactional注解开启事务,该注解可以放在类上和方法上,放在类上时,该类所有的public方法都会开启事务;放在方法上时,表示当前方法支持事务

1
2
3
4
5
@Transactional
@Override
public Map<String, Object> saveResource(MultipartFile file) {
// 相关业务
}

@Transactional 底层执行原理

ProxyTransactionManagementConfiguration 在注入BeanFactoryTransactionAttributeSourceAdvisor 的同时,还为其设置了事务增强器:advisor.setAdvice(transactionInterceptor;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {
@Bean
public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor(TransactionAttributeSource transactionAttributeSource, TransactionInterceptor transactionInterceptor) {
BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
advisor.setTransactionAttributeSource(transactionAttributeSource);
// 设置事务 interceptor
advisor.setAdvice(transactionInterceptor);
if (this.enableTx != null) {
advisor.setOrder((Integer)this.enableTx.getNumber("order"));
}

return advisor;
}

// 创建事务 interceptor
@Bean
public TransactionInterceptor transactionInterceptor(TransactionAttributeSource transactionAttributeSource) {
TransactionInterceptor interceptor = new TransactionInterceptor();
interceptor.setTransactionAttributeSource(transactionAttributeSource);
if (this.txManager != null) {
interceptor.setTransactionManager(this.txManager);
}

return interceptor;
}
}

TransactionInterceptor

image-20230725144817466

TransactionInterceptor 继承自 TransactionAspectSupport,并实现了 MethodInterceptorSerializable;说明它是一个方法拦截器,在声明式事务中,其实就是在IoC容器中注册一个代理对象,当代理对象要执行目标方法时,方法拦截器会工作:

org.springframework.transaction.interceptor.TransactionInterceptor#invoke

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@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, new CoroutinesInvocationCallback() {
@Override
@Nullable
public Object proceedWithInvocation() throws Throwable {
return invocation.proceed();
}
@Override
public Object getTarget() {
return invocation.getThis();
}
@Override
public Object[] getArguments() {
return invocation.getArguments();
}
});
}

关于事务开启、回滚、提交的逻辑就是在 invokeWithinTransaction() 方法中实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,final InvocationCallback invocation) throws Throwable {
...
PlatformTransactionManager ptm = asPlatformTransactionManager(tm);
// 切点
final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);

if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {
// Standard transaction demarcation with getTransaction and commit/rollback calls.
// 创建事务
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;
}
...
}

completeTransactionAfterThrowing() 执行过程中发生异常,执行回滚方法

org.springframework.transaction.interceptor.TransactionAspectSupport#completeTransactionAfterThrowing

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) {
// 判断事务状态不为空的情况下
if (txInfo != null && txInfo.getTransactionStatus() != null) {
// 输出debug日志
if (logger.isTraceEnabled()) {
logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() +
"] after exception: " + ex);
}
// 在指定异常下回滚
if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {
try {
// 开始回滚
txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
}
catch (TransactionSystemException ex2) {
logger.error("Application exception overridden by rollback exception", ex);
ex2.initApplicationException(ex);
throw ex2;
}
catch (RuntimeException | Error ex2) {
logger.error("Application exception overridden by rollback exception", ex);
throw ex2;
}
}
// 不是指定的异常,任然提交
else {
// We don't roll back on this exception.
// Will still roll back if TransactionStatus.isRollbackOnly() is true.
try {
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
catch (TransactionSystemException ex2) {
logger.error("Application exception overridden by commit exception", ex);
ex2.initApplicationException(ex);
throw ex2;
}
catch (RuntimeException | Error ex2) {
logger.error("Application exception overridden by commit exception", ex);
throw ex2;
}
}
}
}

commitTransactionAfterReturning() 正常执行,提交事务方法

org.springframework.transaction.interceptor.TransactionAspectSupport#commitTransactionAfterReturning

1
2
3
4
5
6
7
8
protected void commitTransactionAfterReturning(@Nullable TransactionInfo txInfo) {
if (txInfo != null && txInfo.getTransactionStatus() != null) {
if (logger.isTraceEnabled()) {
logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() + "]");
}
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
}

Transactional 常用属性配置

propagation

@Transactional(propagation = Propagation.REQUIRED)

配置事务的传播特性,默认为:required

传播性 描述
Propagation**.REQUIRED** (默认值)如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务,该设置是最常用的设置
Propagation**.REQUIRES_NEW** 创建新事务,无论当前存不存在事务,都创建新事务,如果当前有事务就将当前事务挂起
Propagation**.SUPPORTS** 如果有事务就在当前事务下运行,没有事务就在无事务状态下运行
Propagation**.NOT_SUPPORTED** 在无事务状态下运行,如果有事务,将当前事务挂起
Propagation**.MANDATORY** 必须存在事务,若无事务,抛出异常IllegalTransactionStateException
Propagation**.NEVER** 不支持事务,有事务就抛出异常
Propagation**.NESTED** 当前有事务就在当事务里面再起一个事务
  • 支持当前事务

int PROPAGATION_REQUIRED = 0; 如果当前存在事务,则加入当前事物;如果当前事务不存在,则新建一个事务;

int PROPAGATION_SUPPORTS = 1; 如果当前存在事务,则加入当前事务;如果当前事务不存在,则以非事务方式运行;

int PROPAGATION_MANDATORY = 2; 如果当前存在事务,则加入当前事务;如果当前事务不存在,则抛出异常;

  • 不支持当前事务

int PROPAGATION_REQUIRES_NEW = 3; 创建一个新事物,如果当前存在新事物,则把当前事务挂起;

int PROPAGATION_NOT_SUPPORTED = 4; 以非事务方式运行,如果当前存在事务,则把当前事务挂起;

int PROPAGATION_NEVER = 5;   以非事务方式运行,如果当前存在事务,则抛出异常;

int PROPAGATION_NESTED = 6;   如果当前存在事务,则创建一个事务作为嵌套事务运行,如果当前不存在事务,则参照PROPAGATION_REQUIRED

对于NESTED事务,如果外部事务异常,则内部事务也必须回滚

isolation

配置事务个隔离级别,默认为当前数据库的默认隔离级别(MySQLREPEATABLE-READ

@Transactional(isolation = Isolation.READ_COMMITTED )

查看数据的隔离级别

img

隔离性 描述
READ UNCOMMITTED(未提交度) 读取未提交内容,所有事务可看到其他未提交事务的结果,很少实际使用(会出现脏读)
READ COMMITTED(提交读) 一个事务只能读取到另一个事务已提交事务的修改过的数据,并且其他事务每次对数据进行修改并提交后,该事务都能查询到最新值
REPEATABLE READ(可重复读) 一个是事务读取其实事务已经提交的修改数据,第一次读取某条记录时,即时其他事务修改了该记录并提交时,之后再读取这条记录时,仍然是第一次读取的值,而不是每次读取不同的数据
SERIALIZABLE(串行化) 事务串行化执行,不会出现踩踏,避免了脏读、幻读和不可重复度,但效率低下
timeout

配置事务超时时间,默认为:-1

@Transactional(timeout = 5)

指定强制回滚之前事务可以占用的时间。单位:秒。如果执行时间草果这个时间就强制回滚

readOnly

@Transactional(readOnly = false)

默认为 :false

指定事务是否为只读,表示这个事务只读取数据但不更新数据,这样可以帮助数据库引擎优化事务。如果只有查询数据操作, 应设置 readOnly = true

rollbackFor

配置在那些异常情况下需要回滚数据,默认情况只在发生不受控异常下回滚(RuntimeExceptionError),开发中最好配置为Exception

受控异常(checked exceptions):就是非运行时异常,即Exception中除了RuntimeException及其子类以外的

不受控异常(unchecked exceptions):RuntimeExceptionError

rollbackFor 属性在这里就可以发挥它的作用了

@Transactional(rollbackFor = Exception.class)

这里你可以使用 java 已声明的异常;也可以使用自定义异常;也可同时设置多个异常类,中间用逗号间隔

@Transactional(rollbackFor = {SQLException.classUserAccountException.class})

noRollbackFor

默认情况下 Spring 的声明式事务对所有的运行时异常进行回滚。可以通过noRollbackFor属性进行设置例外异常
@Transactional(noRollbackFor = {UserAccountException.class})
上面设置了遇到UserAccountException异常不回滚。一般不建议设置这个属性,通常情况下默认即可

注意的几点

  1. @Transactional 只能被应用到public方法上,对于其它非public的方法,如果标记了@Transactional也不会报错,但方法没有事务功能
  2. 用 spring 事务管理器,由spring来负责数据库的打开、提交、回滚。默认遇到运行期例外(throw new RuntimeException("注释");)会回滚,即遇到不受检查(unchecked)的例外时回滚;而遇到需要捕获的例外(throw new Exception("注释");)不会回滚,即遇到受检查的例外时,需我们指定方式来让事务回滚要想所有异常都回滚,要加上 @Transactional( rollbackFor={Exception.class,其它异常}) 。如果让unchecked例外不回滚: @Transactional(notRollbackFor=RunTimeException.class)
  3. @Transactional 注解可以被应用于接口定义和接口方法、类定义和类的 public 方法上。然而,请注意仅仅 @Transactional 注解的出现不足于开启事务行为,它仅仅是一种元数据,能够被可以识别 @Transactional 注解和上述的配置适当的具有事务行为的 beans 所使用
  4. Spring 团队的建议是你在具体的类(或类的方法)上使用 @Transactional 注解,而不要使用在类所要实现的任何接口上。你当然可以在接口上使用 @Transactional 注解,但是这将只能当你设置了基于接口的代理时它才生效。因为注解是不能继承的,这就意味着如果你正在使用基于类的代理时,那么事务的设置将不能被基于类的代理所识别,而且对象也将不会被事务代理所包装(将被确认为严重的)。因此,请接受Spring团队的建议并且在具体的类上使用 @Transactional 注解

事务失效

  1. **propagation**设置错误,包括三种: Propagation.SUPPORTSPropagation.NOT_SUPPORTEDPropagation.NEVER

  2. A,B两个方法,B方法添加了@Transactional,A没有,A中调用了B,如果在调用A方法的过程中,无论是A出问题还是B出问题,抛出异常,都不会回滚,是因为SpringAOP,只有当前事务方法被当前类以外的代码调用时,才会生产代理对象

  3. 加了 try-catch ,没有抛出异常

  4. 两个Service,ServiceA和ServiceB。两个都有@Transactional,A中调用了B,B抛出异常,这时B标识为需要回滚,但是A方法 try-catch 这个异常并进行了处理,A标识为可以 commit,就会报错UnexpectedRollbackException

  5. 数据库引擎不支持事务

  6. rollbackFor设置错误

  7. 抛出检查异常导致事务不能正确回滚(没写rollbackFor,或者 try-catch 掉了没到达外层环绕通知,必须抛出去或者 transactionStatus.setRollbackOnly()

  8. 自定义切面捕获了内层异常(因为优先级问题 事务切面在最外层,可以更改自定义切面order 「不推荐」)

  9. @Transactional 需要加在公共方法上,spring为方法创建代理,添加事务通知前提都是public(解决办法,**public** 或者 annotationTranscationAttributeSource(publicMethodsOnly:true))

  10. 父子容器事务失效,子容器扫描范围过大,将未配置事务控制的service扫描进来(springboot一般不会出现,因为只有一个容器)

  11. 调用本类方法导致传播行为失效,因为调用本类方法不经过代理,因此无法增强(依赖注入自己(代理)调用;通过 aopcontext 拿到代理对象来调用;通过CTW,LTW实现功能增强

  12. @Transactional 没有保证原子行为,**select**方法并不阻塞,事务的原子性仅涵盖 insert update delete select... for update

  13. @Transactional 方法导致 synchronized 失效,**synchronized** 仅仅保证目标方法的原子性,环绕目标方法的还有 commit 等操作,他们并未处于 sync 块内(扩大 synchronized 范围至代理方法调用;使用select...for update 替换 select


Transactional 原理
https://sugayoiya.github.io/posts/49928.html
作者
Sugayoiya
发布于
2021年8月19日
许可协议