介绍:
Spring事务是应用程序处理中非常重要的一部分,它可以保证数据库操作的一致性和可靠性,提高系统的稳定性和性能。事务传播机制是Spring事务管理中的一个核心概念,它定义了在一个方法调用过程中,事务的开始和结束时间以及数据源的绑定方式。本文将会介绍Spring事务传播机制的基本概念和常见的传播机制类型。
Spring的事务,符合ACID标准,也具有标准的事务隔离级别。
事务的传播机制
七种传播机制
1. PROPAGATION_REQUIRED: 这是默认的事务传播行为。如果在当前方法调用时存在一个活动事务,那么它会加入到这个事务中。如果没有活动事务,将会启动一个新的事务。
2. PROPAGATION_SUPPORTS: 该传播行为表示如果存在一个活动事务,则加入该事务。如果没有活动事务,则无须非事务执行。它没有明确的事务边界,因此慎用此传播行为。
3. PROPAGATION_MANDATORY: 这个传播行为表示如果存在一个活动事务,则加入该事务。但如果没有活动事务,则抛出异常。
4. PROPAGATION_REQUIRES_NEW: 这个传播行为始终会启动一个新事务。如果存在一个活动的事务,它会被挂起,直到新事务执行完毕。
5. PROPAGATION_NOT_SUPPORTED: 该传播行为表示总是非事务执行。如果存在一个活动的事务,它将被挂起,直到非事务执行完毕。
6. PROPAGATION_NEVER: 这个传播行为表示必须在非事务环境下运行。如果存在一个活动的事务,则抛出异常。
7. PROPAGATION_NESTED: 此传播行为表示如果存在一个活动事务,则以嵌套事务的方式运行。如果没有活动事务,将会启动一个新的事务。嵌套事务意味着如果内层事务失败,则可以回滚到最近的保存点(savepoint),但外层事务不会受影响。需要注意的是,可能不是所有的数据库都支持嵌套事务,同时如果使用嵌套事务,数据库的JDBC驱动应支持 java.sql.Savepoint。
可以按支持当前事务与否分成三类。
事务传播机制处理演示:
首先,我们创建一个User实体和对应的UserMapper:
// User.java
public class User {
private Long id;
private String name;
private Integer age;
// getter和setter...
}
// UserMapper.java
public interface UserMapper extends BaseMapper<User> {
}
这里提供一个简单的示例,演示使用 Spring 和 MyBatis Plus 实现七种事务传播机制,并模拟抛出异常观察各传播机制在发生错误时的处理结果。
首先,我们需要创建一个模拟错误的方法:
public void createError() {
throw new RuntimeException("模拟抛出错误!");
}
然后,在 UserService 中的每个示例方法中调用 createError()
方法。
事务传播机制及错误处理结果如下:
- PROPAGATION_REQUIRED:
@Transactional(propagation = Propagation.REQUIRED)
public void propagationRequired() {
insertUser("张三", 20);
createError(); // 模拟抛出错误
insertUser("李四", 25);
}
处理结果:张三插入失败,回滚事务。因为 propagateRequired() 方法自身在一个事务中运行,当内部产生异常时,整个事务将回滚。
- PROPAGATION_SUPPORTS:
@Transactional(propagation = Propagation.SUPPORTS)
public void propagationSupports() {
insertUser("王五", 30);
createError(); // 模拟抛出错误
insertUser("赵六", 35);
}
处理结果:如果调用方法的上级方法有事务,那么王五插入失败,回滚事务。如果上级方法没有事务,王五插入成功,赵六插入失败,因为不会影响任何事务。
- PROPAGATION_MANDATORY:
@Transactional(propagation = Propagation.MANDATORY)
public void propagationMandatory() {
insertUser("孙七", 40);
createError(); // 模拟抛出错误
insertUser("周八", 45);
}
处理结果:孙七插入失败,回滚事务。该方法要求上级方法必须存在事务,否则将抛出异常。当内部产生异常时,整个事务将回滚。
- PROPAGATION_REQUIRES_NEW:
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void propagationRequiresNew() {
insertUser("吴九", 50);
createError(); // 模拟抛出错误
insertUser("郑十", 55);
}
处理结果:吴九插入成功,因为该方法总是运行在一个新的事务中,和外部事务无关,抛出异常仅回滚新增的用户郑十。
- PROPAGATION_NOT_SUPPORTED:
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void propagationNotSupported() {
insertUser("陈十一", 60);
createError(); // 模拟抛出错误
insertUser("杨十二", 65);
}
处理结果:陈十一插入成功,杨十二插入失败。该方法运行在无事务环境下,所以陈十一成功插入。抛出异常后,不会影响陈十一的记录。
- PROPAGATION_NEVER:
@Transactional(propagation = Propagation.NEVER)
public void propagationNever() {
insertUser("张十三", 70);
createError(); // 模拟抛出错误
insertUser("李十四", 75);
}
处理结果:张十三插入成功。该方法要求上层方法不能存在事务,否则抛出异常。异常发生后,不会影响张十三的记录。
- PROPAGATION_NESTED:
@Transactional(propagation = Propagation.NESTED)
public void propagationNested() {
insertUser("王十五", 80);
createError(); // 模拟抛出错误
insertUser("赵十六", 85);
}
处理结果:王十五插入失败,回滚事务。该方法如果上级方法有事务,则运行在一个嵌套事务中。当内部产生异常时,只会回滚到该事务的保存点。
事务在什么情况下会失效
在使用 Spring 的默认传播机制(Propagation.REQUIRED
) 时,事务可能在以下情况下失效:
一:未正确使用代理:
下方三个问题本质都是没有正确使用aop代码,spring事务时根据代理来进行传递的,使用事务时务必正确使用代理获取bean。
-
@Transactional
注解不能应用于private
、final
、static
方法。这些方法类型上不能创建代理时,事务将不起作用。 -
方法内部调用:
在一个类中,事务方法的调用是通过方法内部调用的,而没有通过代理对象调用。例如,一个非事务方法调用了一个事务方法,但都在同一个类中,事务将失效。同类方法调用时,不能直接使用this调用。
正确示例:
3. Proxying Mechanism:
如果调用的方法不是由代理对象调用,那么事务会失效。请确保正确使用代理对象。
>检查当前service类是否是通过spring bean加载的。
二:异常类型不匹配:
- 如果方法抛出的异常与事务配置中定义的回滚类型不匹配,事务可能失效。默认情况下,事务会回滚所有运行时异常(
RuntimeException
及其子类)。可以通过@Transactional
的rollbackFor
或noRollbackFor
属性自定义异常类型。
请注意,这里只列出了默认传播机制情况下的失效场景。根据具体的应用需求和代码组织,失效的原因可能会有所不同。在遇到事务失效的问题时,请根据具体情况检查代码和配置,确保遵循正确的设计和实践。