【Spring】深入解析Spring事务:揭秘数据完整性背后的强大引擎

文章目录

  • 1. 事务概述
  • 2. Spring实现事务的方式
    • 2.1 声明式事务
    • 2.2 编程式事务
    • 2.3 注解事务
  • 3. Spring事务的隔离级别
  • 4. Spring事务的传播方式
  • 5. Spring事务的失效场景

1. 事务概述

在MySQL中,事务是指一组数据库操作的集合,这些操作要么全部成功提交,要么全部回滚。事务可以确保数据的一致性和完整性,并提供并发控制机制,以防止数据的损坏或者丢失。

而在Spring中,Spring提供Spring事务机制,用于管理数据库操作或其他资源的一致性和完整性。它允许开发者在应用程序中定义和控制事务,以保证数据的正确处理。


2. Spring实现事务的方式

在Spring中,想要实现事务的管理,通常有一下三种方式

  1. 声明式事务
  2. 注解事务
  3. 编程式事务

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: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"/>

声明式事务的优点:

  1. 低侵入性:业务逻辑不需要关注事务管理的细节,可以将事务逻辑与业务逻辑分离
  2. **可维护性:**事务属性集中配置,易于维护和修改

声明事务的缺点:

  1. **配置复杂:**需要在配置文件中进行额外的配置,相对注解方式更加繁琐
  2. **学习成功高:**初学者可能需要花费一些时间来理解和配置声明式事务

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;
        }
    }
}

编程式事务的优点:

  1. **灵活性:**程序员可以更加精确地控制事务的边界,可以根据具体需求决定何时开始、提交、回滚。
  2. **细粒度控制:**可以将事务范围限制在必要的操作上,减少锁定时间和并发冲突的机会,从而提高性能
  3. **异常处理:**可以在捕获异常后决定是否回滚事务、重试操作或采取其他特定的错误处理策略。

编程式事务的缺点:

  1. 代码侵入:增加代码的复杂性和可读性,并且可能导致事务管理的逻辑分散在多个地方,使得代码难以维护
  2. **重复劳动:**导致大量重复的代码,并增加出错的机会
  3. **缺乏声明式:**编程式事务缺乏清晰的注解或配置来描述事务的属性。这使得事务的管理和配置更容易出错,特别是在涉及到多个方法或跨越多个层级的事务时。

2.3 注解事务

通过注解标记需要事务的方法或类,Spring通过Transactional注解来指定事务的传播方式、隔离级别等。

@Transactional(rollbackFor = Exception.class)
@Override
public void createActivity(Request req) {
	.....
}

注解事务的优点

  1. **简单快速:**使用注解可以很容易地将事务应用到方法上,而无需编写额外的代码
  2. **灵活性:**可以根据具体方法的需求灵活配置事务属性

注解事务的缺点

  1. **侵入性:**需要在业务逻辑中添加注解,导致业务与事务管理的耦合程度增加
  2. **可读性降低:**事务的一些细节信息可能分散在各个方法上,不方便全局查看

3. Spring事务的隔离级别

总所周知,在MySQL事务中有四个隔离级别,分别是读未提交、读已提交、可重复读、串行化。

而在Spring中,可以通过通过Transactional的注解的isolation指定隔离级别:

  1. DEFAULT:Spring中默认的事务隔离级别,Spring中默认的事务隔离级别
  2. READ_UNCOMMITTED:读未提交
  3. READ_COMMITTED:读已提交
  4. REPEATABLE_READ:可重复读
  5. SERIALIZABLE:串行化

4. Spring事务的传播方式

Spring的事务传播方式用于指定方法调用时的事务边界行为。它决定了一个方法被另一个方法调用时,如何处理事务的传播。

通过@Transactional注解,可以在方法或类级别上指定事务的传播方式。该注解可以应用于方法或类上,并通过propagation属性指定事务的传播方式。

Spring中有多个传播方式:

  1. REQUIRED(默认值):如果当前存在事务,则加入该事务;如果不存在事务,则创建一个新的事务。这是最常用的传播方式。
  2. SUPPORTS:如果当前存在事务,则加入该事务;如果不存在事务,则以非事务方式执行。
  3. MANDATORY:如果当前存在事务,则加入该事务;如果不存在事务,则抛出异常
  4. REQUIRES_NEW:无论当前是否存在事务,都会创建一个新的事务;如果存在事务,则将当前事务挂起。
  5. NOT_SUPPORTED:以非事务方式执行操作;如果当前存在事务,则将当前事务挂起。
  6. NEVER:以非事务方式执行操作;如果当前存在事务,则抛出异常
  7. NESTED:如果当前存在事务,则在嵌套事务内执行;嵌套事务可以独立提交或回滚,而不影响外部事务;如果外部事务提交,则嵌套事务也必须提交才能生效。

5. Spring事务的失效场景

使用Spring进行事务管理的时候,难免会出现事务失效的情况,以下是几种事务失效的场景:

  1. 访问权限问题:Spring要求被代理方法必须是public的
  2. 方法用 final 修饰 : spring 事务底层使用了 AOP,通过 jdk 动态代理或者 cglib;如果某个方法用 final 修饰了,那么在它的代理类中,就无法重写该方法,而添加事务功能
  3. 同一类中的自身方法调用:Spring声明式事物是基于AOP实现的,是使用动态代理来达到事物管理的目的;因为调用这个方法的是this,没有经过 Spring 的代理类。
    4.数据库本身不支持事务:例如使用了MyISAM 引擎,该引擎是不支持事务操作的,要使用支持事务的InnoDB引擎,或者使用编程式事务,或将内部调用改为外部调用
  4. 事务没有被Spring管理:包含的事务的类通过@service、@component等注解修饰即可。
  5. 使用cglib代理:单纯使用cglib代理是不会出现事务失效的情况,只有当接口层使用声明式事务同时使用了cglib代理才会出现事务失效的情况
  6. rollbackFor异常指定错误:明确需要回滚的异常,正确设置rollbackFor的异常class。
  7. 异常被catch住了:要么不catch需要回滚的异常,要么catch之后再抛出。

你可能感兴趣的:(SSM,spring,数据库,java)