详细解析 Spring 的 @Transactional 注解的工作原理及其如何确保数据库操作在同一事务内执行。
chou403
/ Spring
/ c:
/ u:
/ 9 min read
@Transactional 原理 为什么加上这个 就会实现所有数据库操作在一起
@Transactional
是 Spring 中用于声明式事务管理的注解。当在方法或类上使用 @Transactional
时,Spring 会确保该方法内的所有数据库操作在一个事务中执行。实现这个功能的原理涉及到 Spring 的事务管理机制,AOP(面向切面编程)和代理模式。
@Transactional 的工作原理
-
AOP(面向切面编程):
@Transactional
注解使用 AOP 来拦截方法调用。- Spring AOP 通过动态代理拦截目标方法的执行,在方法执行之前,之后以及异常发生时,插入事务管理逻辑。
-
代理模式:
- Spring 使用 JDK 动态代理或 CGLIB 代理来创建目标对象的代理实例。
- 当调用代理对象的方法时,代理对象会拦截调用,并在适当的时机管理事务。
-
事务管理器:
@Transactional
依赖于 Spring 的PlatformTransactionManager
来管理事务。- 事务管理器负责开启,提交和回滚事务,具体实现包括
DataSourceTransactionManager
(用于 JDBC),JpaTransactionManager
(用于 JPA)等。
事务传播机制
@Transactional
提供多种事务传播机制,决定了当一个事务方法调用另一个事务方法时,事务如何传播。常见的传播行为包括:
- REQUIRED(默认): 如果当前没有事务,就新建一个事务;如果已经存在一个事务中,加入到这个事务中。
- REQUIRES_NEW: 总是新建一个事务。如果当前已经存在一个事务,暂停当前事务。
- MANDATORY: 使用当前的事务,如果当前没有事务,就抛出异常。
- SUPPORTS: 如果当前有事务,就使用事务;如果当前没有事务,就以非事务方式执行。
- NOT_SUPPORTED: 总是以非事务方式执行,如果当前存在事务,就暂停当前事务。
- NEVER: 总是以非事务方式执行,如果当前存在事务,则抛出异常。
- NESTED: 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则新建一个事务。
实现原理详解
-
代理对象的创建:
- Spring 在创建 Bean 的时候,会检测 Bean 类或其方法是否标注了
@Transactional
注解。 - 如果检测到
@Transactional
注解,Spring 会为该 Bean 创建一个代理对象。 - 代理对象可以是基于接口的 JDK 动态代理,也可以是基于类的 CGLIB 代理。
- Spring 在创建 Bean 的时候,会检测 Bean 类或其方法是否标注了
-
拦截方法调用:
- 当调用
@Transactional
标注的方法时,代理对象会拦截方法调用。 - 代理对象会在方法执行前调用事务管理器,开启一个新的事务(如果没有事务存在)。
- 然后调用目标方法。
- 当调用
-
事务的提交与回滚:
- 如果目标方法执行成功,代理对象会在方法执行后提交事务。
- 如果目标方法抛出异常,代理对象会回滚事务。
@Transactional 示例代码
下面是一个简单的示例,展示了 @Transactional
的使用和事务管理的过程:
@Service
public class MyService {
@Autowired
private MyRepository myRepository;
@Transactional
public void performDatabaseOperations() {
myRepository.save(new Entity(...));
myRepository.update(...);
myRepository.delete(...);
// 其他数据库操作
}
}
在这个例子中,performDatabaseOperations
方法上的 @Transactional
注解确保了所有的数据库操作在一个事务中执行。如果方法执行过程中出现异常,事务会回滚,所有操作都会撤销。
@Transactional 总结
@Transactional
通过 AOP,代理模式和事务管理器的结合,确保标注的方法在一个事务中执行,从而实现了所有数据库操作要么一起成功,要么一起失败的事务特性。这种机制大大简化了开发者的工作,使得事务管理变得更加透明和易于使用。
事务传播行为有哪些
事务传播行为(Transaction Propagation Behavior)定义了在一个事务中,当方法调用另一个方法时,事务如何传播。Spring 提供了多种事务传播行为,以应对不同的业务场景。以下是 Spring 中常见的事务传播行为:
1. REQUIRED
- 描述: 如果当前没有事务,就新建一个事务。如果已经有一个事务在运行中,加入这个事务。
- 应用场景: 这是默认的传播行为,适用于大多数情况。通常用于需要确保所有操作在同一个事务中的情况。
2. REQUIRES_NEW
- 描述: 总是新建一个新的事务。如果当前已经有一个事务,则先挂起当前事务。
- 应用场景: 用于需要一个全新事务的情况,即使当前已经存在一个事务。例如,记录审计日志时,不希望日志记录受主事务的影响。
3. SUPPORTS
- 描述: 如果当前有事务,就在事务中运行。如果当前没有事务,就以非事务方式运行。
- 应用场景: 用于既可以在事务环境中执行,也可以在非事务环境中执行的情况。
4. NOT_SUPPORTED
- 描述: 总是以非事务方式运行。如果当前有事务,则挂起当前事务。
- 应用场景: 用于不需要事务管理的操作,或者操作必须在非事务环境中执行的情况。
5. MANDATORY
- 描述: 如果当前有事务,就在事务中运行。如果当前没有事务,则抛出异常。
- 应用场景: 用于必须在现有事务中运行的方法。如果没有事务环境,则认为是错误的调用。
6. NEVER
- 描述: 总是以非事务方式运行。如果当前有事务,则抛出异常。
- 应用场景: 用于必须确保方法不在事务中运行的情况。
7. NESTED
- 描述: 如果当前有事务,则在嵌套事务中运行。如果当前没有事务,则新建一个事务。
- 应用场景: 用于需要嵌套事务的情况。嵌套事务允许部分回滚,即内部事务可以回滚而外部事务可以继续。
示例代码
下面是一个示例代码,展示了如何使用不同的事务传播行为:
@Service
public class TransactionalService {
@Transactional(propagation = Propagation.REQUIRED)
public void methodWithRequired() {
// Method implementation
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void methodWithRequiresNew() {
// Method implementation
}
@Transactional(propagation = Propagation.SUPPORTS)
public void methodWithSupports() {
// Method implementation
}
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void methodWithNotSupported() {
// Method implementation
}
@Transactional(propagation = Propagation.MANDATORY)
public void methodWithMandatory() {
// Method implementation
}
@Transactional(propagation = Propagation.NEVER)
public void methodWithNever() {
// Method implementation
}
@Transactional(propagation = Propagation.NESTED)
public void methodWithNested() {
// Method implementation
}
}
这些传播行为提供了灵活的事务管理策略,可以根据具体的业务需求选择合适的传播行为,从而确保数据的一致性和完整性。