SpringTransaction

SpringTransaction

Spring 事务

事务的特性(ACID)

原子性(Atomicity):

一个事务(transaction)中的所有操作,或者全部完成,或者全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。即,事务不可分割、不可约简。

一致性(Consistency):

在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设约束、触发器、级联回滚等。

隔离性(Isolation):

数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括未提交读(Read uncommitted)、提交读(read committed)、可重复读(repeatable read)和串行化(Serializable)。

持久性(Durability):

事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。

Spring 事务处理官方有直观的解释:“Spring Transaction 中,事务被抽象为事务管理器(TransactionManager),而事务管理器管理着相应的事务。具体到数据库操作,事务在 Spring 中的使用方式是框架负责开启/关闭事务,而开发人员只需要在需要事务管理的方法或类上加上对应的注解即可。” 。

事务的使用

Spring 事务的使用方式主要通过以下三种:

1. 编程式事务:

基于 TransactionTemplate 编写代码,可以显式地编写事务处理逻辑。

    @Autowired
    private TransactionTemplate transactionTemplate;
    public void testTransaction() {
            transactionTemplate.execute(new TransactionCallbackWithoutResult() {
                @Override
                protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
                    try {
                        // ....  业务代码
                    } catch (Exception e){
                        //回滚
                        transactionStatus.setRollbackOnly();
                    }

                }
            });
    }

2. 声明式事务:

基于 XML 或注解的方式,在方法或类级别上定义事务属性。Spring 框架会自动代理,实现切面编程,实现事务代理。

在具体的实现中,一般使用注解方式,在类或方法上通过@Transactional注解定义事务。

事务属性详解

实际业务开发中,一般都是使用 @Transactional 注解来开启事务。

1.propagation:事务传播属性

  • REQUIRED(默认)

    如果当前方法已经在一个事务中,则该事务将被沿用;否则将创建一个新的事务。这是最常用的传播属性,它确保在整个调用链中只有一个事务

  • SUPPORTS

    如果当前方法在一个事务中,则该事务将被沿用;否则将在没有事务的情况下执行该方法。这种传播行为适用于不需要强制要求事务的方法。

  • MANDATORY

    如果当前方法在一个事务中,则该事务将被沿用;否则将抛出异常。这种传播行为适用于需要强制要求事务的方法,如果没有事务则会抛出异常。

  • REQUIRES_NEW

    将创建一个新的事务,如果当前方法已经在一个事务中,则该事务将被挂起,直到新事务完成。这种传播行为适用于需要在新事务中执行的方法,而不受外部事务的影响。

  • NOT_SUPPORTED

    如果当前方法在一个事务中,则该事务将被挂起,直到方法执行完成;否则将在没有事务的情况下执行该方法。这种传播行为适用于不希望方法在事务中执行的方法。

  • NEVER

    如果当前方法在一个事务中,则抛出异常;否则将在没有事务的情况下执行该方法。这种传播行为适用于希望方法绝不在事务中执行的方法。

  • NESTED

    如果当前方法在一个事务中,则创建一个嵌套事务,并在该嵌套事务中执行方法;否则将创建一个新的事务,并在其中执行该方法。如果外部事务提交,则嵌套事务将一起提交;如果外部事务回滚,则嵌套事务将回滚,但不影响外部事务。这种传播行为适用于需要在嵌套事务中执行的方法,它可以看作是 REQUIRED 传播属性的子级

注意:propagation 属性只影响当前方法调用期间的事务传播行为,并不影响调用方或被调用方的事务传播行为。因此,在使用 propagation 属性时,需要对整个方法调用链的事务传播行为有一个全局的认识。

2.isolation:事务隔离级别

  • DEFAULT

    使用数据库默认的隔离级别。这种隔离级别通常是由数据库驱动程序或数据库本身提供的。

  • READ_UNCOMMITTED

    最低的隔离级别,允许一个事务读取另一个事务未提交的数据。这种隔离级别可能会导致“脏读”、“不可重复读”和“幻读”等问题。

  • READ_COMMITTED

    允许一个事务读取另一个事务已经提交的数据。这种隔离级别避免了“脏读”,但仍然可能会导致“不可重复读”和“幻读”等问题。

  • REPEATABLE_READ

    确保在一个事务中多次读取同一行的结果始终相同。这种隔离级别避免了“不可重复读”,但仍然可能会导致“幻读”等问题。

  • SERIALIZABLE

    最高的隔离级别,通过确保事务串行执行来避免所有可能的并发问题。这种隔离级别可以避免所有并发问题,但是性能较低,通常不建议在高并发系统中使用。

注意:事务隔离级别越高,事务的并发性越低,性能也越差。因此,在选择事务隔离级别时,需要在并发性和数据一致性之间进行权衡,并根据具体的业务场景和性能需求进行选择。

3.timeout:事务超时时间

事务超时时间。如果事务超过了指定的时间而没有完成,则事务将被回滚。可以使用timeout属性来设置事务的超时时间,单位为秒。

4.rollbackFor:需要回滚的异常类

需要回滚的异常类。如果事务遇到指定的异常类,将会回滚事务。可以使用rollbackFor属性来指定需要回滚的异常类。

5.noRollbackFor:不需要回滚的异常类

不需要回滚的异常类。如果事务遇到指定的异常类,将不会回滚事务。可以使用noRollbackFor属性来指定不需要回滚的异常类。

其他

  • 脏读(Dirty Read)

    一个事务读取了另一个事务未提交的数据,并且在这个数据被回滚之后产生不一致的结果。举个例子:事务A更新一个数据并将其标记为“正在处理中”,但事务A还未提交事务,事务B读取到了这个数据并使用它。如果事务A最终回滚了,那么事务B所使用的数据将会是不一致的。

  • 不可重复读(Non-repeatable Read)

    一个事务多次读取同一行数据,但在读取之间,另一个事务修改或删除了这行数据。这可能会导致第一个事务读取的结果发生变化,从而导致不一致的结果。

  • 幻读(Phantom Read):

    一个事务执行了一个查询,并得到了一些行,然后另一个事务插入或删除了一些与该查询匹配的行。当第一个事务再次执行相同的查询时,它会发现有更多或更少的行,从而产生不一致的结果。

参考