文章目录
1. 事务概述
在MySQL中,事务是指一组数据库操作的集合,这些操作要么全部成功提交,要么全部回滚。事务可以确保数据的一致性和完整性,并提供并发控制机制,以防止数据的损坏或者丢失。
而在Spring中,Spring提供Spring事务机制,用于管理数据库操作或其他资源的一致性和完整性。它允许开发者在应用程序中定义和控制事务,以保证数据的正确处理。
2. Spring实现事务的方式
在Spring中,想要实现事务的管理,通常有一下三种方式
- 声明式事务
- 注解事务
- 编程式事务
2.1 声明式事务
声明式事务是指在配置文件中声明事务的属性,需要配置事务管理器(例如 DataSourceTransactionManager
)和事务通知(例如 TransactionInterceptor
)。
并且需要在配置文件中指定哪些方法需要进行事务配置。
<!-- 数据源配置 -->
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mydb"/>
<property name="username" value="root"/>
<property name="password" value="password"/>
</bean>
<!-- 事务管理器配置 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 配置事务通知 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!-- 设置事务传播行为 -->
<tx:attributes>
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="delete*" propagation="REQUIRED"/>
<tx:method name="get*" propagation="SUPPORTS" read-only="true"/>
<tx:method name="find*" propagation="SUPPORTS" read-only="true"/>
</tx:attributes>
</tx:advice>
<!-- 配置AOP切面 -->
<aop:config>
<aop:pointcut id="serviceMethods" expression="execution(* com.example.service.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="serviceMethods"/>
</aop:config>
<!-- 配置扫描注解事务 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
声明式事务的优点:
- 低侵入性:业务逻辑不需要关注事务管理的细节,可以将事务逻辑与业务逻辑分离
- **可维护性:**事务属性集中配置,易于维护和修改
声明事务的缺点:
- **配置复杂:**需要在配置文件中进行额外的配置,相对注解方式更加繁琐
- **学习成功高:**初学者可能需要花费一些时间来理解和配置声明式事务
2.2 编程式事务
编程式事务是指通过代码手动管理事务的方式,直接在业务逻辑中决定什么时候开启事务、什么时候提交事务、什么时候回滚事务。
@Service
public class TransactionalService {
private final PlatformTransactionManager transactionManager;
private final SomeRepository someRepository;
@Autowired
public TransactionalService(PlatformTransactionManager transactionManager, SomeRepository someRepository) {
this.transactionManager = transactionManager;
this.someRepository = someRepository;
}
public void performTransactionalOperation() {
// 创建一个事务定义
DefaultTransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
transactionDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
// 获取事务
TransactionStatus transactionStatus = transactionManager.getTransaction(transactionDefinition);
try {
// 在事务中执行数据库操作
someRepository.save(...);
someRepository.delete(...);
// 提交事务
transactionManager.commit(transactionStatus);
} catch (Exception e) {
// 发生异常时回滚事务
transactionManager.rollback(transactionStatus);
throw e;
}
}
}
编程式事务的优点:
- **灵活性:**程序员可以更加精确地控制事务的边界,可以根据具体需求决定何时开始、提交、回滚。
- **细粒度控制:**可以将事务范围限制在必要的操作上,减少锁定时间和并发冲突的机会,从而提高性能
- **异常处理:**可以在捕获异常后决定是否回滚事务、重试操作或采取其他特定的错误处理策略。
编程式事务的缺点:
- 代码侵入:增加代码的复杂性和可读性,并且可能导致事务管理的逻辑分散在多个地方,使得代码难以维护
- **重复劳动:**导致大量重复的代码,并增加出错的机会
- **缺乏声明式:**编程式事务缺乏清晰的注解或配置来描述事务的属性。这使得事务的管理和配置更容易出错,特别是在涉及到多个方法或跨越多个层级的事务时。
2.3 注解事务
通过注解标记需要事务的方法或类,Spring通过Transactional
注解来指定事务的传播方式、隔离级别等。
@Transactional(rollbackFor = Exception.class)
@Override
public void createActivity(Request req) {
.....
}
注解事务的优点
- **简单快速:**使用注解可以很容易地将事务应用到方法上,而无需编写额外的代码
- **灵活性:**可以根据具体方法的需求灵活配置事务属性
注解事务的缺点
- **侵入性:**需要在业务逻辑中添加注解,导致业务与事务管理的耦合程度增加
- **可读性降低:**事务的一些细节信息可能分散在各个方法上,不方便全局查看
3. Spring事务的隔离级别
总所周知,在MySQL事务中有四个隔离级别,分别是读未提交、读已提交、可重复读、串行化。
而在Spring中,可以通过通过Transactional
的注解的isolation
指定隔离级别:
DEFAULT
:Spring中默认的事务隔离级别,Spring中默认的事务隔离级别READ_UNCOMMITTED
:读未提交READ_COMMITTED
:读已提交REPEATABLE_READ
:可重复读SERIALIZABLE
:串行化
4. Spring事务的传播方式
Spring的事务传播方式用于指定方法调用时的事务边界行为。它决定了一个方法被另一个方法调用时,如何处理事务的传播。
通过@Transactional
注解,可以在方法或类级别上指定事务的传播方式。该注解可以应用于方法或类上,并通过propagation
属性指定事务的传播方式。
Spring中有多个传播方式:
REQUIRED(默认值)
:如果当前存在事务,则加入该事务;如果不存在事务,则创建一个新的事务。这是最常用的传播方式。SUPPORTS
:如果当前存在事务,则加入该事务;如果不存在事务,则以非事务方式执行。MANDATORY
:如果当前存在事务,则加入该事务;如果不存在事务,则抛出异常REQUIRES_NEW
:无论当前是否存在事务,都会创建一个新的事务;如果存在事务,则将当前事务挂起。NOT_SUPPORTED
:以非事务方式执行操作;如果当前存在事务,则将当前事务挂起。NEVER
:以非事务方式执行操作;如果当前存在事务,则抛出异常NESTED
:如果当前存在事务,则在嵌套事务内执行;嵌套事务可以独立提交或回滚,而不影响外部事务;如果外部事务提交,则嵌套事务也必须提交才能生效。
5. Spring事务的失效场景
使用Spring进行事务管理的时候,难免会出现事务失效的情况,以下是几种事务失效的场景:
- 访问权限问题:Spring要求被代理方法必须是public的
- 方法用 final 修饰 : spring 事务底层使用了 AOP,通过 jdk 动态代理或者 cglib;如果某个方法用 final 修饰了,那么在它的代理类中,就无法重写该方法,而添加事务功能
- 同一类中的自身方法调用:Spring声明式事物是基于AOP实现的,是使用动态代理来达到事物管理的目的;因为调用这个方法的是this,没有经过 Spring 的代理类。
4.数据库本身不支持事务:例如使用了MyISAM 引擎,该引擎是不支持事务操作的,要使用支持事务的InnoDB引擎,或者使用编程式事务,或将内部调用改为外部调用 - 事务没有被Spring管理:包含的事务的类通过@service、@component等注解修饰即可。
- 使用cglib代理:单纯使用cglib代理是不会出现事务失效的情况,只有当接口层使用声明式事务同时使用了cglib代理才会出现事务失效的情况
- rollbackFor异常指定错误:明确需要回滚的异常,正确设置rollbackFor的异常class。
- 异常被catch住了:要么不catch需要回滚的异常,要么catch之后再抛出。