Spring的事务

Spring的事务

!!!个人见解,仅供参考。

一、什么是事务?

事务是一组操作,它们被当作一个单独的执行单元,要么全部成功执行,要么全部失败执行。事务具有四个基本特性,通常被称为 ACID 特性:

  • 原子性(Atomicity):事务是一个不可分割的工作单位,要么全部执行成功,要么全部失败回滚。
  • 一致性(Consistency):事务执行后,数据库的状态应该与执行前的状态保持一致。
  • 隔离性(Isolation):多个事务并发执行时,各自的操作互不干扰,每个事务感觉不到其他事务的存在。
  • 持久性(Durability):事务一旦提交,其结果应该永久保存在数据库中,即使系统发生故障。

二、事务的作用

引入事务是为了确保数据库操作的一致性、可靠性和完整性。事务是一组数据库操作,它们被视为一个单独的工作单元,要么全部执行成功,要么全部失败回滚,以保证数据库的一致性。

  1. 保证数据一致性:事务可以确保数据库在任何时候都保持一致状态。即使在多个操作同时进行的情况下,事务可以使数据在操作之间保持一致,避免数据不一致的情况发生。
  2. 支持并发控制:在多用户环境中,可能会有多个用户同时对数据库进行读写操作。事务可以通过并发控制机制来管理多个并发操作,确保数据的完整性和一致性。
  3. 保证数据的完整性:事务可以确保数据操作的完整性,即当一组操作执行失败时,可以回滚事务,使数据库恢复到操作之前的状态,避免数据丢失或损坏。
  4. 提高系统性能:通过使用事务,可以减少数据库锁的持有时间,从而降低了系统的资源竞争,提高了系统的并发性能。

综上所述,引入事务可以提高数据库操作的可靠性和性能,确保数据的一致性和完整性,是数据库管理中的重要概念之一。

三、Spring为什么要提供事务管理?

  1. 简化事务管理:事务管理是应用程序开发中的一个常见需求,但手动管理事务的代码通常会导致代码的重复和冗长。Spring 提供了简洁的 API 和声明式事务管理的方式,使得开发者可以更轻松地管理事务,将事务逻辑与业务逻辑分离,提高了代码的可读性和可维护性。
  2. 提高数据一致性和完整性:通过事务管理,Spring 可以确保一组数据库操作要么全部成功提交,要么全部回滚,从而保证了数据的一致性和完整性。这对于涉及多个数据库操作的业务逻辑尤为重要,可以避免出现数据不一致的情况。
  3. 支持多种事务管理方式:Spring 提供了多种事务管理方式,包括编程式事务管理和声明式事务管理。开发者可以根据实际需求选择合适的事务管理方式,从而灵活地应对不同的业务场景。
  4. 与其他 Spring 特性的集成:Spring 的事务管理与其他 Spring 特性(如 Spring MVCSpring Data 等)紧密集成,可以轻松地与这些特性结合使用,提供全面的解决方案。
  5. 跨多种数据访问技术的支持:Spring 的事务管理可以跨多种数据访问技术,包括 JDBC、Hibernate、JPA 等,使得开发者可以在不同的数据访问层面使用统一的事务管理方式。

综上所述,Spring 提供事务管理的目的是为了简化开发者处理事务的复杂性,确保数据的一致性和完整性,并提供灵活的事务管理方式,以满足不同业务场景的需求。

四、Spring事务的使用

1、Spring事务入门

目前流行的方式通过@Transactional注解来实现声明式事务管理。以下是一个简单的示例,展示了如何在 Spring 中配置和使用声明式事务管理:

@Service
public class StudentServiceImpl extends ServiceImpl<StudentMapper, Student>
        implements StudentService {
    @Autowired
    private StudentMapper studentMapper;

    @Override

    @Transactional
    public void addDoubleStudent() {

        Student s1 = Student.builder()
                .money(100.0)
                .name("张三")
                .build();
        Student s2 = Student.builder()
                .money(100.0)
                .name("王五")
                .build();
        //分别向数据库插入两个学生
        studentMapper.insert(s1);
        //发生异常
        int i = 1 / 0;
        studentMapper.insert(s2);
    }
}

如果没有加事务的话,只有 studentMapper.insert(s1)会成功,然后遇到int i = 1 / 0就会抛出ArithmeticException异常从而方法停止,那么studentMapper.insert(s2)就失败了。如果在方法上加上 @Transactional注解的话,这个方法就是一个事务,根据事务的特性,那么两个插入操作都会失败,数据就会回滚。

2、@Transactional的详细说明

@Transactional注解用于实现声明式事务管理。通过在方法或类级别添加@Transactional注解,可以告诉Spring框架将这些方法或类的操作纳入事务管理的范围内。

下面是@Transactional注解的一些重要细节:

  1. 在方法级别使用@Transactional注解可以添加在类的方法上,以声明该方法应该在一个事务中执行。当方法被调用时,Spring框架会在方法执行之前开启一个事务,在方法执行之后根据方法的执行情况来提交或回滚事务。
  2. 在类级别使用:除了在方法级别使用外,@Transactional注解还可以添加在类上。这意味着该类的所有公共方法都将在事务中执行。类级别的注解可以用于指定默认的事务传播行为和事务管理器。
  3. 设置事务属性@Transactional注解提供了一系列属性,用于配置事务的行为,比如传播行为(Propagation)、隔离级别(Isolation)、超时时间(Timeout)、只读状态(Read-only)等。
  4. 传播行为(Propagation):传播行为定义了方法中的事务如何与外部事务交互。例如,REQUIRES_NEW表示每次调用该方法都会创建一个新的事务,而REQUIRES_PROPAGATION表示该方法会加入一个已经存在的事务中,如果不存在事务,则创建一个新的事务。
  5. 异常处理:默认情况下,当被注解的方法抛出一个未检查异常(RuntimeException)时,事务会被回滚。但你可以通过rollbackFornoRollbackFor属性来指定特定的异常应该导致事务回滚或不回滚。
  6. 注解驱动事务@Transactional注解是基于AOP(面向切面编程)实现的。Spring通过代理模式在运行时为被注解的方法创建一个事务代理,在方法执行前后添加事务管理逻辑。
  7. 事务管理器@Transactional注解依赖于配置好的事务管理器来实际地管理事务。Spring框架提供了不同类型的事务管理器,如基于JDBC的、基于JPA的、基于Mybatis的等。
  8. 应用范围@Transactional注解可以应用于Spring的各个组件,包括Spring MVC的Controller、Service层、DAO层等。

3、事务的传播行为

4、事务的隔离级别

事务的隔离级别就是只事务与事务之间的隔离程度。TransactionDefinition中定义了五种隔离基本。如图:

在MySQL中,默认的事务隔离级别是可重复读(Repeatable Read)。

5、@Transactional的详细使用

在 Spring 中,@Transactional 注解用于声明一个方法应该被包装在事务中。这个注解提供了多个属性来配置事务的行为。以下是 @Transactional 注解的一些常用成员:

  1. value:可用于指定要应用的事务管理器的名称。
  2. transactionManager:指定要用于事务管理的 Bean 名称。通常与 value 属性一起使用,用于明确指定事务管理器。
  3. propagation:指定事务的传播行为,默认值是 Propagation.REQUIRED。可以使用上述提到的 7 种传播行为之一。
  4. isolation:指定事务的隔离级别,默认是 Isolation.DEFAULT。可以选择 Isolation 枚举中的值,如 Isolation.READ_COMMITTEDIsolation.REPEATABLE_READ 等。
  5. timeout:指定事务的超时时间,单位是秒。如果方法执行时间超过指定的时间,事务将被自动回滚。
  6. readOnly:指定事务是否为只读。如果设置为 true,表示这个事务只读取数据但不修改数据,可以帮助提升性能。
  7. rollbackFor:用于指定哪些异常触发事务回滚。默认情况下,Spring 只回滚 RuntimeException 和其子类异常,以及 Error 和其子类异常。如果需要对其他异常也进行回滚,可以在这个属性中指定。
  8. noRollbackFor:与 rollbackFor 相反,用于指定哪些异常不触发事务回滚。
  9. rollbackForClassName:与 rollbackFor 类似,但是使用异常类名来指定需要回滚的异常。
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED, readOnly = true, timeout = 30, rollbackFor = {SQLException.class, MyException.class})
public void myTransactionalMethod() {
    // 业务逻辑
}

在这个例子中,myTransactionalMethod()方法被设置为在一个REQUIRED传播行为下执行,使用READ_COMMITTED隔离级别,只读模式为true,超时时间为30秒,如果遇到SQLException或MyException异常则回滚事务。

五、Spring编程式事务管理(可跳过)

编程式事务管理是手动编码是实现事务的开始、提交、回滚等操作,而声明式事务管理是基于Spring AOP技术实现的。(大多数场景声明式事务管理就够了,在事务层次复杂的情况下才推荐编程事)!!!!!!!

1、Spring编程式事务管理的快速入门

 @Autowired
    private TransactionTemplate transactionTemplate;
    @Override
    public void addStudent() {

        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
                try {
                    // ....  业务代码
                    studentMapper.insert(s1);
                    int i = 1 / 0;
                    studentMapper.insert(s2);
                } catch (Exception e) {
                    //回滚
                    transactionStatus.setRollbackOnly();
                }

            }
        });
    }

这段代码是一个典型的 Spring Framework 中使用的事务管理模板。让我解释一下它的作用:

  1. transactionTemplate.execute():这是一个事务模板的方法调用,它可以帮助你在代码中执行一段逻辑,并在一个事务内进行管理。
  2. TransactionCallbackWithoutResult:这是一个 Spring 提供的回调接口,用于执行一段逻辑,它的 doInTransactionWithoutResult 方法会在事务内执行。
  3. doInTransactionWithoutResult(TransactionStatus transactionStatus):这是 TransactionCallbackWithoutResult 接口的方法,它定义了在事务内执行的逻辑。
  4. transactionStatus.setRollbackOnly():如果在执行业务逻辑时发生了异常,这行代码将会设置事务的回滚状态,这样事务就会在执行完成后回滚,保证数据的一致性和完整性。

因此,这段代码的作用是在一个事务内执行一段业务逻辑,并在发生异常时进行事务回滚,以确保数据的完整性。

2、相关源码的分析

编程式事务的实现主要涉及到以下几个核心类:

  1. PlatformTransactionManager:事务管理器接口,定义了对事务进行操作的方法,如开始事务、提交事务、回滚事务等。
  2. TransactionDefinition:事务定义接口,定义了事务的一些属性,如事务的隔离级别、超时时间、是否只读等。
  3. TransactionStatus:事务状态接口,定义了事务的当前状态,如是否已经开始、是否已经回滚等。
  4. TransactionTemplate 是 Spring 框架提供的一个便捷的编程式事务管理工具类,用于简化编程式事务的操作。它封装了事务的开始、提交、回滚等操作(也就是封装了PlatformTransactionManager里面的操作)。

TransactionTemplate的方法,如图所示:

TransactionTemplate中的核心方法execute()

主要的业务代码就就是调用TransactionCallback 接口中的doInTransaction方法。目前只有TransactionCallbackWithoutResult类实现了TransactionCallback接口,TransactionCallbackWithoutResult如图所示:

可以看到这是一个抽象类,doInTransaction()方法中调用了doInTransactionWithoutResult(TransactionStatus transactionStatus)。这段代码是一个23种设计模式中的模板方法模式的典型实现,用于执行事务的回调操作。

3. 待补充…

六、Spring事务失效的场景总结

1.未加@Transactional注解或者没有被Spring管理

如果目标类没有被Spring管理那么即使有@Transactional注解也会失效。

如果方法未使用 @Transactional 注解声明,那么该方法将不会被 Spring 的事务管理器拦截,导致事务不生效。解决方法是在需要事务管理的方法上添加 @Transactional 注解。

@Transactional
public void someTransactionalMethod() {
    // 业务逻辑
}

2.事务注解位置错误

@Transactional 注解应该放在公有方法上,如果放在私有方法、静态方法、final的方法或者内部方法上,可能会导致事务失效。解决方法是确保 @Transactional 注解放在合适的位置。
像IDEA这样的工具都会提示的。如图所示:

save会受到事务管理但是query就不会受到事务的管理,数据库也不会回滚。



public void query(Student s1) {
        studentMapper.insert(s2);
    	save(s1);
    }

    @Transactional
    public void save(Student s1) {
        studentMapper.insert(s1);
        throw new RuntimeException();
    }

3.默认的事务传播行为不适用

@Transactional 注解有一个 propagation 属性,用于指定事务的传播行为。如果使用了不适合的传播行为,可能会导致事务失效。需要根据业务逻辑来选择合适的传播行为。这种错误在某些场景极为隐秘和复杂,很难察觉,在事务层次复杂的业务推荐使用编程式事务

4.异常处理不当

异常处理不当在使用@Transactional注解时可能会导致事务不正确地提交或回滚,从而导致数据一致性问题。默认情况下,Spring的@Transactional注解只会在方法抛出RuntimeExceptionError时回滚事务,而不会回滚受检异常。

  • 适当地处理异常:在方法中捕获异常后,要确保以合适的方式进行处理。如果可以处理异常并且继续正常执行,确保事务的一致性。但如果异常表示了一个无法继续执行的严重问题,应该重新抛出异常以触发事务回滚。

  • 重新抛出异常:当捕获到的异常无法在当前方法中处理时,应该考虑重新抛出异常以触发事务回滚。可以使用throw语句重新抛出异常,或者声明方法抛出异常并让调用者处理。

  • 使用rollbackFor属性@Transactional注解提供了rollbackFor属性,可以指定哪些异常需要触发事务回滚。通过在@Transactional注解中设置rollbackFor属性,可以确保捕获到指定类型的异常时触发事务回滚。

示例代码:

@Service
@Transactional(rollbackFor = Exception.class) // 指定捕获到任何异常时都回滚事务
public class MyService {

    @Autowired
    private MyRepository myRepository;

    public void myMethod() {
        try {
            // 可能会抛出异常的操作
            myRepository.someMethod();
        } catch (Exception e) {
            // 异常处理逻辑
            // 如果需要回滚事务,则重新抛出异常
            throw e;
        }
    }
}

5.数据库不支持事务

在MySql老的版本是用的是MyISAM作为默认引擎,它不支持事务,会导致是事务失效。Mysql的新的版本MySql支持了InnoDB,它支持是事务。(目前推荐使用5.7之后的版本)。默认也是开启的。在mysql的配置文件里面可以配置默认的数据库引擎库。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值