Spring 框架的事务管理是企业级应用开发的核心功能之一,它通过统一的事务抽象层简化了不同数据访问技术(如 JDBC、Hibernate、JPA 等)的事务管理。以下是 Spring 事务的核心要点:
1. 事务核心接口组件
1.1 PlatformTransactionManager
事务管理的核心接口
public interface PlatformTransactionManager extends TransactionManager {
TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException;
void commit(TransactionStatus status) throws TransactionException;
void rollback(TransactionStatus status) throws TransactionException;
}
针对不同的数据源有不同具体实现(策略模式)
重要实现类:DataSourceTransactionManager
1.2 TransactionDefinition
- 该接口中定义了事务的传播行为、隔离级别、超时时间、只读模式等属性
1.2.1 传播行为详解
这一部分只看定义会有些抽象,可以在第三部分大小事务处理中加深理解
- 常用传播行为(
Propagation
事务边界):REQUIRED
(默认):如果当前存在事务,则加入;否则新建事务REQUIRES_NEW
:始终新建事务,挂起当前事务(如果存在)SUPPORTS
:如果有事务则加入,否则以非事务方式运行NOT_SUPPORTED
:以非事务方式运行,挂起当前事务(如果存在)MANDATORY
:必须存在事务,否则抛出异常NEVER
:必须在非事务环境下运行,否则抛出异常
其中 TransactionTemplate 编程式事务就是其实现类,在构造方法中可以通过TransactionDefinition 提供这些事务属性。
1.3 TransactionOperations
如上图所示,是 TransactionTemplate 的另一个接口,提供 execute 方法来执行事务操作。通过 TransactionCallback 回调来执行实际的业务逻辑,并根据异常或事务状态来决定是否提交或回滚事务。
2. 事务管理方式
2.1 编程式事务
- 通过
TransactionTemplate
手动控制事务边界- 在实例构造时,通过 TransactionDefinition 传入边界属性
- 在代码中通过 try catch 灵活声明触发回滚的异常
- 示例代码:
@Autowired
private TransactionTemplate transactionTemplate;
public void doSomething() {
transactionTemplate.execute(status -> {
try {
// 业务逻辑
return result;
} catch (Exception e) {
status.setRollbackOnly(); // 标记回滚
return null;
}
});
}
2.2 声明式事务
- 通过
@Transactional
注解或 XML 配置声明事务行为 - 基于 AOP 实现,对业务代码无侵入
- 需要注意只能用于 public 方法
- 示例:
@Service
public class UserService {
@Transactional(
propagation = Propagation.REQUIRED,
isolation = Isolation.READ_COMMITTED,
timeout = 30,
rollbackFor = {SQLException.class}
)
public void updateUser(User user) {
// 数据库操作
}
}
2.2.1 @Transactional
注解详解
// 类/接口上,方法上
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {
// 指定 Bean 名称,多用于多数据源的场景的依赖倒置
@AliasFor("transactionManager")
String value() default "";
@AliasFor("value")
String transactionManager() default "";
String[] label() default {};
// 事务传播行为,控制事务的边界逻辑
Propagation propagation() default Propagation.REQUIRED;
// 事务隔离级别,同 innoDB 的四个隔离级别 (默认为数据库默认级别)
Isolation isolation() default Isolation.DEFAULT;
// 事务超时时间
int timeout() default -1;
String timeoutString() default "";
boolean readOnly() default false;
// 会触发回滚的异常类型,默认仅回滚 `RuntimeException` 和 `Error`(非检查异常)
Class<? extends Throwable>[] rollbackFor() default {};
String[] rollbackForClassName() default {};
// 不会触发回滚的异常类型
Class<? extends Throwable>[] noRollbackFor() default {};
String[] noRollbackForClassName() default {};
}
2.2.2 声明式事务实现原理
- 代理机制:通过 AOP(动态代理) 对
@Transactional
注解的方法进行增强 - 事务拦截器:
TransactionInterceptor
负责事务的开启、提交和回滚 - 代理类型:
- 如果目标类实现接口,默认使用 JDK 动态代理
- 否则使用 CGLIB 代理
3. 事务场景
3.1 大小事务的处理
1. 场景明晰
- 父事务(大事务):通常是一个外层事务,它控制着多个业务操作,可能涉及多个方法调用。父事务负责整个操作的一致性。如果父事务中有一个子事务失败,父事务可以决定是否回滚整个事务。
- 子事务(小事务)**:是父事务内部执行的子操作,它们可以作为嵌套事务或独立事务存在。子事务可以通过不同的传播行为与父事务相互作用,甚至在子事务回滚时,不影响父事务的状态。
一个例子:在一个大型的金融应用中,可能有多个步骤需要执行,如订单创建、支付、发货等。每个步骤都可以看作一个子事务,而整个订单的创建、支付流程可以看作一个父事务。在这种情况下,如果子事务失败,整个父事务也会失败(除非设置了 REQUIRES_NEW 或 NESTED 等传播行为)。
@Transactional
public void parentTransaction() {
// 父事务逻辑
childTransaction(); // 调用子事务
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void childTransaction() {
// 子事务逻辑
// 即使子事务失败,父事务依然可以继续
throw new RuntimeException("子事务失败");
}
2. 传播行为如何进行大小事务的回滚处理
Spring 事务是通过不同的传播行为(propagation)来区分对大小事务的不同处理的。基于这个大小事务的场景,会对下面三个传播行为有更好的认识:
- REQUIRED(默认传播行为):如果当前存在事务,则加入当前事务;如果没有事务,则创建一个新的事务。此时,子事务的回滚会导致整个父事务回滚,子事务和父事务是同一个事务。
- NESTED:如果当前有事务,则在当前事务中嵌套一个事务,并且子事务的回滚不会影响父事务的提交。只有子事务的异常抛出并导致事务回滚,才会使得父事务回滚。
- REQUIRES_NEW:总是创建一个新的事务,并挂起当前事务。无论父事务是否成功,子事务的提交或回滚都不会影响父事务。
3. 总结
- REQUIRED:子事务回滚时,父事务也回滚(同一个事务)。
- REQUIRES_NEW:子事务回滚时,不影响父事务,父事务继续执行。
- NESTED:子事务回滚时,只有子事务会回滚,父事务不会受到影响,除非父事务也发生回滚。
因此,在处理大小事务回滚时,需要根据具体的业务需求来选择合适的事务传播行为。如果需要精细的事务控制,可以选择 NESTED 或 REQUIRES_NEW,确保子事务的失败不会影响父事务的执行。