Spring-事务基础与原理

Spring 事务的核心原理是通过AOP (面向切面编程) 在方法执行前后进行拦截,并在拦截器中实现事务的开启、提交或回滚。具体来说,Spring 事务基于 PlatformTransactionManager 接口和 TransactionInterceptor 类来管理事务。

事务基本概念

事务通常指的是数据库事务(Database Transaction),其具备 ACID 特性:

  • A(Atomicity)原子性:事务中的操作要么全部成功,要么全部失败。
  • C(Consistency)一致性:事务执行前后,数据保持一致状态。
  • I(Isolation)隔离性:多个事务并发执行时,彼此隔离。
  • D(Durability)持久性:事务提交后,结果是永久性的。

在一个事务执行过程中会获得的互斥锁,只有事务提交或失败之后才会释放,在此期间其他事务只能读,不能写。这是隔离性的关键,一般数据都定义了如下四种事务隔离级别:

  • 串行化:指对同一行记录,读写操作都会加锁。当出现读写锁冲突的时候,后访问的事务必须等待前一个事务执行完成,才能继续执行
  • 可重复度读:一个事务执行过程中看到的数据,总是与这个事务在启动的时候看到的数据是一致的,在可重复读的隔离级别下,未提交变更对其它事务是不可见的
  • 读已提交:一个事务提交之后,它做的变更才会被其他事务看到
  • 读未提交:一个事务还没有提交,它做的变更就能被其他的事务看到

Spring事务特性

Spring为事务提供了完整的支持,在Spring事务实现中提供了两种事务管理方式:

  • 编程式事务管理:通过 TransactionTemplate 或直接使用底层的 PlatformTransactionManager 来控制事务。
  • 声明式事务管理:通过注解(如 @Transactional)或 XML 配置来实现事务管理。

Spring 事务示例

声明式事务 - @Transactional

1
2
3
4
5
6
7
8
@Service
public class TransactionExample{
@Transactional
public void declarativeTransaction(){
update1();
update2();
}
}

@Transactional 注解作用于方法之上,在底层实现上相当于使用 BEGIN 开启了事务,在执行完方法后,使用 COMMIT 提交事务。

1
2
3
4
5
6
7
8
@Service
@Transactional
public class TransactionExample{
public void declarativeTransaction(){
update1();
update2();
}
}

@Transactional 注解作用于类上,在当前类中所有的 public 方法都开启了事务。

编程式事务 - TransactionTemplate

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class TransactionExample{
private final TransactionTemplate transactionTemplate;

public TransactionExample(TransactionTemplate transactionTemplate){
this.transactionTemplate = transactionTemplate;
}

public void programmaticTransaction() {
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
try {
update1();
update2();
} catch (Exception e) {
log.error("execute failed: {}", e.getCause());
status.setRollbackOnly();
}
}
});
}
}

两种事务管理方式对比

声明式事务 编程式事务
使用方法 @Transactional TransactionTemplate
优点 使用简单 可以控制事务提交的开启和提交时机,能够更小粒度的控制事务的范围,也更加直观
缺点 使用不当事务可能失效;多个事务性操作可能导致事务无法正常提交,导致长事务 需要硬编码来控制事务
适用场景 同一个方法中,事务操作比较多 当事务操作的数量很少

在合适的场景使用合适的事务管理方式非常重要,在一些场景下,当对事务操作非常频繁,特别是在递归、外部通讯等耗时的场景中使用事务,很有可能就会引发长事务,那么应该考虑将非事务的部分放在前面执行,最后在写入数据环节时再开启事务。

Spring 事务隔离级别

TransactionDefinition 接口中定义了五个表示隔离级别的常量:

  • TransactionDefinition.ISOLATION_DEFAULT:这是默认值,表示使用底层数据库的默认隔离级别。对大部分数据库而言,通常这值就是TransactionDefinition. ISOLATION_READ_COMMITTED。
  • TransactionDefinition.ISOLATION_READ_UNCOMMITTED:该隔离级别表示一个事务可以读取另一个事务修改但还没有提交的数据。该级别不能防止脏读,不可重复读和幻读,因此很少使用该隔离级别。比如PostgreSQL实际上并没有此级别。
  • TransactionDefinition.ISOLATION_READ_COMMITTED:该隔离级别表示一个事务只能读取另一个事务已经提交的数据。该级别可以防止脏读,这也是大多数情况下的推荐值。
  • TransactionDefinition.ISOLATION_REPEATABLE_READ:该隔离级别表示一个事务在整个过程中可以多次重复执行某个查询,并且每次返回的记录都相同。该级别可以防止脏读和不可重复读。
  • TransactionDefinition.ISOLATION_SERIALIZABLE:所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。

Spring事务传播行为

Spring 事务的传播行为(Propagation Behavior)定义了当一个事务方法被另一个事务方法调用时,两者之间应该如何交互。Spring 提供了多种事务传播行为,每种都有其特定的应用场景和特性。Spring中规定了7种类型的事务传播特性:

事务传播行为类型 说明
Propagation.REQUIRED 如果当前没有事务,就新建一个事务,如果已经存在一个事务中;就加入到这个事务中,这是事务传播行为的默认值
Propagation.REQUIRES_NEW 总是创建新的事务,新的事务回滚不会影响原来的事务
Propagation.NESTED 如果已经存在事务,则使用嵌套事务,如果当前没有事务,则新建事务
Propagation.MANDATORY 使用当前事务,如果当前没有事务,就抛出异常
Propagation.NEVER 以非事务方式执行操作,如果当前存在事务,则抛出异常
Propagation.NOT_SUPPORTED 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起,执行结束之后再恢复事务
Propagation.SUPPORTS 如果当前存在事务则加入,如果当前没有事务,就以非事务方式执行

Propagation.REQUIRED

Propagation.REQUIRED@Transactional 的默认值,这是最常用的传播行为,适用于大多数需要事务管理的场合,尤其是希望多个操作能够在同一个事务中执行以确保数据的一致性时。它有如下特点:

  • 如果当前没有物理事务,那么Spring会创建一个新的事务
  • 如果当前已经存在了一个物理事务,那么有@Transactional(propagation = Propagation.REQUIRED)注解的方法就会加入这个物理事务
  • 每一个有@Transactional(propagation = Propagation.REQUIRED)注解的方法,都对应一个逻辑事务,这些逻辑事务会加入到同一个物理事务
  • 每个逻辑事务都有自己的作用范围,但是在这种传播机制下,所有这些范围都会被映射到同一个物理事务中

Propagation.REQUIRES_NEW

总是创建新的事务,新的事务回滚不会影响原来的事务。当希望某个方法独立于外部事务进行提交或回滚时使用。即使外部事务回滚,这个新事务内的操作仍然可以成功提交,反之亦然。

在这种隔离级别下,每个物理事务都有自己的数据库连接,也就是说,当创建内部的物理事务的时候,会同步为这个事务绑定一个新的数据库连接。当内部的物理事务运行的时候,外部的物理事务的就会暂停执行(保持连接),当内部的物理事务提交之后,外部的时候恢复运行,继续执行提交或回滚操作。

另外,在这种传播级别下,即便内部的物理事务回滚,外部的物理事务也会正常提交,如果外部的物理事务在内部的物理事务提交之后回滚,内部的物理事务并不会受到任何影响。

Propagation.NESTED

如果已经存在事务,则使用嵌套事务,如果当前没有事务,则新建事务。Propagation.NESTED 与PROPAGATION_REQUIRED比较类似,只是会使用保存点(savepoint),换句话说,内部逻辑事务可以部分回滚。savepoint是数据库事务中的子事务,事务可以回滚到savepoint而不影响savepoint创建前的变化,而不是回滚整个事务。支持嵌套事务的数据库较少,因此它的应用范围有限。主要用于需要部分回滚功能的场景。

Propagation.MANDATORY

使用当前事务,如果当前没有事务,就抛出异常:

1
org.springframework.transaction. IllegalTransactionStateException: No existing transaction found for transaction marked with propagation 'mandatory'.

因为外部已经存在了物理事务(通过Propagation. REQUIRED创建),那么内部事务将会加入这个事务,如果内部事务回滚了,外部事务也会回滚,这一点和 Propagation.REQUIRED 相同。用于强制必须在一个事务中执行的方法,确保这些方法总是在事务环境下被调用。这有助于避免意外地在无事务环境中执行重要操作。

Propagation.NEVER

以非事务方式执行操作,如果当前存在事务,则抛出异常。

1
org.springframework.transaction. IllegalTransactionStateException: Existing transaction found for transaction marked with propagation 'never'

当调用有@Transactional(propagation = Propagation.NEVER)注解的方法,一定要确保没有打开任何物理事务。

Propagation.NOT_SUPPORTED

以非事务方式执行操作,如果当前存在事务,就把当前事务挂起,执行结束之后再恢复事务。用于那些不应该在事务中执行的操作,比如简单的查询操作,避免因为事务锁导致性能下降。

Propagation.SUPPORTS

如果当前存在事务则加入,如果当前没有事务,就以非事务方式执行。需要注意的是,即便当前的事务被挂起了,也应该避免执行运行时间很长的任务,这是因为被挂起的事务的数据库连接还是激活的状态,这意味着,数据库连接池无法重用这个连接。 适合于那些不需要严格事务保证的操作,例如只读查询。这种设置允许在没有事务的情况下更高效地执行某些操作,同时也能在有事务的情况下共享事务资源。

Spring事务实现原理

Spring 事务管理通过AOP、PlatformTransactionManager 接口和事务传播行为等机制,实现了对事务的自动化管理。

spring事务核心接口

事务操作相关的API:

  • Spring事务@Enanle模块驱动:@EnableTranSactionManagement
  • Spring事务注解:@Transactional
  • Spring事务事件监听器:@TransactionalEventListener

事务抽象相关的API

  • Spring平台事务管理器:PlatformTransactionManager
  • Spring事务定义:TransactionDefinition
  • Spring事务状态:TransactionStatus
  • Spring事务代理配置:ProxyTransactionManagementConfiguration

AOP相关的API:

  • Spring事务PointcutAdvisor实现:BeanFactoryTransactionAttrubuteSourceAdvisor
  • Spring事务MethodInterceptor实现:TransactionInterceptor
  • Spring事务属性源:TransactionAttributeSource

其中,PlatformTransactionManager、TransactionDefinition、TransactionStatus最为重要,他们之间的关系如下:

PlatformTransactionManager

PlatformTransactionManager是Spring对于事务模型的抽象,它代表事务的整体执行过程。通常事务都应用在关系型数据库中,Spring对于事务的读写的模型做了更高层次的抽象,使得其可以应用在任何需要数据一致性的场景,比如JMX等,Spring将这些场景统一的抽象为commit和rollback两个核心方法。

1
2
3
4
5
6
7
8
public interface PlatformTransactionManager extends TransactionManager {

TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException;

void commit(TransactionStatus status) throws TransactionException;

void rollback(TransactionStatus status) throws TransactionException;
}
如果有@Transactional注解的方法,如果他们对应的传播行为不同,那么其对应的TransactionDefinition也是不同的,这也就是说getTransaction这个方法获取到的并不是物理事务,而是某个具体方法的逻辑事务,同理,commit和rollback也是对应的这个逻辑事务。

TransactionDefinition

TransactionDefinition是事务的元信息定义,类似于Spring IOC中BeanDefinition。实际上,Spring中事务的定义参考了EJB中对于事务的定义,TransactionDefinition的核心方法有:

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 interface TransactionDefinition {
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;
int ISOLATION_DEFAULT = -1;
int ISOLATION_READ_UNCOMMITTED = 1;
int ISOLATION_READ_COMMITTED = 2;
int ISOLATION_REPEATABLE_READ = 4;
int ISOLATION_SERIALIZABLE = 8;
int TIMEOUT_DEFAULT = -1;

default int getPropagationBehavior() {
return PROPAGATION_REQUIRED;
}

default int getIsolationLevel() {
return ISOLATION_DEFAULT;
}

default int getTimeout() {
return TIMEOUT_DEFAULT;
}

default boolean isReadOnly() {
return false;
}

@Nullable
default String getName() {
return null;
}

static TransactionDefinition withDefaults() {
return StaticTransactionDefinition.INSTANCE;
}
}

TransactionStatus

事务分为逻辑事务和物理事务,逻辑事务是指代码中事务的操作;物理事务是通过数据库连接来获取相关的物理的连接以及相关的数据库的事务。TransactionStatus是用来描述当前逻辑事务的执行情况,其核心方法及含义:

1
2
3
4
5
6
7
8
public interface TransactionStatus extends TransactionExecution, SavepointManager, Flushable {
default boolean hasSavepoint() {
return false;
}

default void flush() {
}
}

从Spring5.2开始,对这个接口进行了拆分,将部分方法放置在了TransactionExecution中:

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
public interface TransactionExecution {
default String getTransactionName() {
return "";
}

default boolean hasTransaction() {
return true;
}

default boolean isNewTransaction() {
return true;
}

default boolean isNested() {
return false;
}

default boolean isReadOnly() {
return false;
}

default void setRollbackOnly() {
throw new UnsupportedOperationException("setRollbackOnly not supported");
}

default boolean isRollbackOnly() {
return false;
}

default boolean isCompleted() {
return false;
}
}

Spring事务实现原理分析

Spring事务管理的核心在于其提供的抽象层,它通过PlatformTransactionManager接口统一了事务操作,让开发者可以方便地在不同的数据访问技术之间切换而无需修改业务逻辑代码。同时,通过AOP机制,Spring能够无缝地将事务管理集成到服务层方法调用中,简化了事务编程模型。下面是简单时序图:

简单逻辑:

  • 代理创建:在Spring应用启动阶段,如果启用了事务管理(例如通过@EnableTransactionManagement),Spring会为带有@Transactional注解的Bean创建代理对象。这个过程通常涉及到AOP自动代理的创建。
  • 事务开始:当调用被@Transactional标记的方法时,Spring的AOP代理会拦截该方法调用,并根据TransactionDefinition创建事务(通过调用PlatformTransactionManager.getTransaction())。如果当前存在事务且传播行为允许,则加入现有事务;否则,创建新事务。
  • 事务执行与状态维护:在事务执行期间,TransactionStatus对象会被用来跟踪事务的状态变化,比如设置回滚标志等。
  • 事务提交/回滚:方法正常返回后,Spring会尝试提交事务(调用PlatformTransactionManager.commit())。如果方法抛出异常,且异常类型符合回滚条件(默认为unchecked异常),则会触发事务回滚(调用PlatformTransactionManager.rollback())。

@Transaction声明式事务实现分析

  • @Transaction定义:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@Reflective
public @interface Transactional {
@AliasFor("transactionManager")
String value() default "";
@AliasFor("value")
String transactionManager() default "";
String[] label() default {};
Propagation propagation() default Propagation.REQUIRED;
Isolation isolation() default Isolation.DEFAULT;
int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;
String timeoutString() default "";
boolean readOnly() default false;
Class<? extends Throwable>[] rollbackFor() default {};
String[] rollbackForClassName() default {};
Class<? extends Throwable>[] noRollbackFor() default {};
String[] noRollbackForClassName() default {};
}

如果需要在SpringBoot中要使用@Transactional,我们需要添加@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 {
boolean proxyTargetClass() default false;

AdviceMode mode() default AdviceMode.PROXY;

int order() default Ordered.LOWEST_PRECEDENCE;
}

@EnableTransactionManagement这个注解会通过@Import自动装配到TransactionManagementConfigurationSelector实现:

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

@Override
protected String[] selectImports(AdviceMode adviceMode) {
return switch (adviceMode) {
case PROXY -> new String[] {AutoProxyRegistrar.class.getName(),
ProxyTransactionManagementConfiguration.class.getName()};
case ASPECTJ -> new String[] {determineTransactionAspectClass()};
};
}

private String determineTransactionAspectClass() {
return (ClassUtils.isPresent("jakarta.transaction.Transactional", getClass().getClassLoader()) ?
TransactionManagementConfigUtils.JTA_TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME :
TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME);
}
}

进入代理模式的配置类ProxyTransactionManagementConfiguration ,其是一个标准的SpringBoot的配置类:

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
@Configuration(proxyBeanMethods = false)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
@ImportRuntimeHints(TransactionRuntimeHints.class)
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {

@Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor(
TransactionAttributeSource transactionAttributeSource, TransactionInterceptor transactionInterceptor) {

BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
advisor.setTransactionAttributeSource(transactionAttributeSource);
advisor.setAdvice(transactionInterceptor);
if (this.enableTx != null) {
advisor.setOrder(this.enableTx.<Integer>getNumber("order"));
}
return advisor;
}

@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public TransactionAttributeSource transactionAttributeSource() {
// Accept protected @Transactional methods on CGLIB proxies, as of 6.0.
return new AnnotationTransactionAttributeSource(false);
}

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

通过配置项将注解解析添加到 AnnotationTransactionAttributeSource,转换为 BeanFactoryTransactionAttributeSourceAdvisor对象,最终生成事务代理对象。

事务执行核心逻辑

前面提到Spring事务是基于AOP实现的,通过配置加载最终会生成TransactionInterceptor,在事务执行过程中会AOP执行器链执行到 TransactionInterceptor#invoke():

1
2
3
4
5
6
7
8
9
10
11
@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, invocation::proceed);
}

进入 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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
@Nullable
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
final InvocationCallback invocation) throws Throwable {

// If the transaction attribute is null, the method is non-transactional.
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 rtm) {
// reactive 事务
}

PlatformTransactionManager ptm = asPlatformTransactionManager(tm);
final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);

if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager cpptm)) {
// 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 (retVal != null && txAttr != null) {
TransactionStatus status = txInfo.getTransactionStatus();
if (status != null) {
if (retVal instanceof Future<?> future && future.isDone()) {
try {
future.get();
}
}
....
}
}
// 正常提交
commitTransactionAfterReturning(txInfo);
return retVal;
}
// 异常回滚
else {
Object result;

......
return result;
}
}

protected TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm,
@Nullable TransactionAttribute txAttr, final String joinpointIdentification) {
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);
}
}
return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
}

上述代码的整体逻辑为:

  • 获取事务管理器
  • 视情况创建事务
  • 执行业务方法
  • 当捕捉到异常判断是否回滚处理
  • 无异常时判断是否提交处理
commitTransactionAfterReturning(txInfo) 事务提交
1
2
3
4
5
protected void commitTransactionAfterReturning(@Nullable TransactionInfo txInfo) {
if (txInfo != null && txInfo.getTransactionStatus() != null) {
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
}
  • commit():
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
@Override
public final void commit(TransactionStatus status) throws TransactionException {
processCommit(defStatus);
}

private void processCommit(DefaultTransactionStatus status) throws TransactionException {
try {
boolean beforeCompletionInvoked = false;
boolean commitListenerInvoked = false;

try {
boolean unexpectedRollback = false;
prepareForCommit(status);
triggerBeforeCommit(status);
triggerBeforeCompletion(status);
beforeCompletionInvoked = true;

if (status.hasSavepoint()) {

unexpectedRollback = status.isGlobalRollbackOnly();
this.transactionExecutionListeners.forEach(listener -> listener.beforeCommit(status));
commitListenerInvoked = true;
status.releaseHeldSavepoint();
}
else if (status.isNewTransaction()) {

unexpectedRollback = status.isGlobalRollbackOnly();
this.transactionExecutionListeners.forEach(listener -> listener.beforeCommit(status));
commitListenerInvoked = true;
// 最终提交事务,由具体的 ORM 框架实现
doCommit(status);
}
else if (isFailEarlyOnGlobalRollbackOnly()) {
unexpectedRollback = status.isGlobalRollbackOnly();
}
}
.....
}
}

总结

事务失效问题分析

  • 方法内部调用: @Transactional 是通过AOP来实现,如果在一个类内部调用则为 this.xxxMethod(),这里this并不是一个代理对象,因此会失效。
  • 访问权限不正确:被 @Transactional 修饰的方法为 private, 刚事务会失效。在配置加载的时候只判断public方法:if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers()))
  • 方法使用final修饰: 通过cglib是通过生成子类来方式生成代理类,事务方法被定义为final,则意味着该方法无法被重写,无法添加事务功能。
  • 未被Spring管理
  • 多线程调用:事务有一个很重要的特性,就是不能跨线程使用
  • 存储引擎不支持事务
  • 错误的传播特性
  • 异常处理不正确:@Transactional默认捕获的异常是RuntimeException和Error,如果抛出的是其他类型异常,则会导致事务无法回滚。

实践经验

在日常的开发中,也最好遵循一些业界探索出的实践经验,以确保事务的正确管理和数据的一致性:

  • 明确标记事务边界:使用@Transactional注解明确标记需要进行事务管理的方法。将注解放在方法上,以确保在方法执行期间启用事务管理。
  • 设置适当的事务传播行为和隔离级别:根据业务需求,选择适当的事务传播行为和隔离级别。确保在不同的方法调用中正确管理事务的传播和隔离。
  • 限制事务作用范围:将事务的作用范围限制在需要进行事务管理的最小代码块上,而不是整个方法或类。这样可以减少锁定资源的时间和范围,提高并发性能。
  • 避免长时间事务:长时间事务会占用数据库资源并降低系统性能。尽量将事务的执行时间控制在合理的范围内,避免长时间的事务操作。
  • 捕获并处理异常:在事务方法中捕获并处理异常是必要的。根据业务需求,选择适当的处理方式,如回滚事务、记录日志或抛出自定义异常。
  • 尽量避免嵌套事务:嵌套事务会增加事务管理的复杂性,并可能导致死锁等并发问题。除非必要,尽量避免使用嵌套事务。
  • 注意事务的回滚规则:通过设置rollbackFor属性,明确指定哪些异常会触发事务的回滚。确保异常的正确处理和事务的正确回滚。
  • 定期进行性能调优和监控:对于事务频繁的应用程序,定期进行性能调优和监控是必要的。通过监控事务执行时间、数据库锁定情况等指标,优化事务管理和数据库设计。
  • 编写单元测试验证事务管理:编写单元测试来验证事务管理的正确性。使用Spring的测试框架和事务支持来模拟数据库操作和事务回滚,确保事务管理的正确性。

Spring-事务基础与原理
http://example.com/2025/06/11/spring-事务基础与原理/
作者
ares
发布于
2025年6月11日
许可协议