分享

(十二)Spring从入门到入土——Spring事务

 小样样样样样样 2021-05-21

Spring事务

事务是逻辑上的一组操作,要么都执行,要么都不执行

事务的特性(ACID)

  • 原子性

  • 一致性

  • 隔离性

  • 持久性

Spring管理事务的方式有几种

程序是否支持事务是取决于数据库是否支持事务

MySQL是如何保证原子性的:

恢复机制是依赖回滚日志实现的

  • 编程式事务,在代码中硬编码(不推荐使用)

  • 声明式事务,在配置文件中配置(推荐使用),代码侵入性小,通过AOP实现

    • 基于XML的声明式事务

    • 基于注解的声明式事务

Spring事务管理接口介绍

Spring中事务管理相关的最重要的三个接口如下

  • PlatformTransactionManager:(平台)事务管理器,Spring事务策略的核心(上层管理者)

  • TransactionDefinition:事务定义信息(事务隔离级别、传播行为、超时、只读、回滚规则)

  • TransactionStatus:事务运行状态

PlatformTransactionManager:事务管理接口

Spring并不直接管理事务,而是提供了多种事务管理器。spring通过该事务管理接口为多个平台提供了对应的事务管理器,具体的实现就是各个平台自己的事情了。

该接口主要是将事务管理行为抽象出来,然后不同的平台去实现它,可以保证提供给外部的行为不变,方便我们扩展。

TransactionDefinition:事务属性

该类定义了一些基本的事务属性。

  • 事务属性包括了五个方面

    • 隔离级别

    • 传播行为

    • 回滚规则

    • 是否只读

    • 事务超时

TransactionStatus:事务状态

该接口用来记录事务的状态,该接口定义了一组方法,用来获取或判断事务的相应状态信息。

public interface TransactionStatus{
    boolean isNewTransaction(); // 是否是新的事务
    boolean hasSavepoint(); // 是否有恢复点
    void setRollbackOnly();  // 设置为只回滚
    boolean isRollbackOnly(); // 是否为只回滚
    boolean isCompleted; // 是否已完成
}

事务属性详解

Spring事务传播行为(枚举类:Propagation)

事务传播行为是为了解决业务层之间互相调用的事务问题。

当事务方法被另一个事务方法调用时,必须制定事务应该如何传播。例如:方法可能继续在现有的事务中运行,也可能开启一个新事务,并在自己的事务中运行。

事务传播行为类型说明是否支持当前事务
REQUIRED如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。(默认情况)
SUPPORTS如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
MANDATORY如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。(mandatory:强制性)
REQUIRES_NEW创建一个新的事务,如果当前存在事务,则把当前事务挂起。
NOT_SUPPORTED**以非事务方式运行,如果当前存在事务,则把当前事务挂起。
NEVER以非事务方式运行,如果当前存在事务,则抛出异常。
NESTED如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于REQUIRED。

Spring事务中的隔离级别(枚举类:Isolation)

TransactionDefinition接口中定义了五个表示隔离级别的常量。

  • ISOLATION_DEFAULT: 后端数据库默认的隔离级别,Mysql 默认采用的 REPEATABLE_READ隔离级别 Oracle 默认采用的 READ_COMMITTED隔离级别.

  • ISOLATION_READ_UNCOMMITTED: 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读

  • ISOLATION_READ_COMMITTED: 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生

  • ISOLATION_REPEATABLE_READ: 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。

  • ISOLATION_SERIALIZABLE: 最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。

事务超时属性

所谓事务超时,就是指一个事务所允许执行的最长时间,如果超过该时间限制但事务还没有完成,则自动回滚事务。在TransactionDefinition中以int的值来表示超时时间,其单位是秒,默认值是-1

事务只读属性

对于只有读取数据查询的事务,可以指定事务类型为readonly,即只读属性。只读事务不涉及数据的修改,数据库会提供一些优化手段,适合用在有多条数据库查询操作的方法中。

MySQL默认对每一个新建立的连接都启用了autocommit模式,在该模式下,每一个发送到MySQL服务器的sql语句都会在一个单独的事务中处理,执行结束后会自动提交事务,并开启一个新事务。

如果不加Transaction,每条sql会开启一个单独的事务,中间被其他事务改了数据,都会实时读取到最新值。

如果你一次执行多条查询语句,例如统计查询,报表查询,在这种场景下,多条查询 SQL 必须保证整体的读一致性,否则,在前条 SQL 查询之后,后条 SQL 查询之前,数据被其他用户改变,则该次整体的统计查询将会出现读数据不一致的状态,此时,应该启用事务支持

事务回滚规则

这些规则定义了那些异常会导致事务回滚而那些不会。默认情况下,事务只有遇到了运行时异常(RuntimeException的子类)时才会回滚,Error也会导致事务回滚,但是在遇到检查型(Checked)异常时不会回滚。

如果要定义你回滚的异常类型可以这样

@Transactional(rollbackFor = MyException.class)

@Transaction注解

作用范围

  • 方法:推荐将注解使用在方法上,不过需要注意的是,该注解只能应用到public方法上,否则不生效

  • 类:如果定义在类上,说明对该类中的所有public方法有有效

  • 接口:不推荐在接口上使用

常用配置参数

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {

    @AliasFor("transactionManager")
    String value() default "";

    @AliasFor("value")
    String transactionManager() default "";

    Propagation propagation() default Propagation.REQUIRED;

    Isolation isolation() default Isolation.DEFAULT;

    int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;

    boolean readOnly() default false;

    Class<? extends Throwable>[] rollbackFor() default {};

    String[] rollbackForClassName() default {};

    Class<? extends Throwable>[] noRollbackFor() default {};

    String[] noRollbackForClassName() default {};

}

@Transactional的常用配置参数总结

属性名说明
propagation事务的传播方式,默认REQUIRED
isolation事务的隔离级别,默认DEFAULT
timeout事务超时时间,默认-1(不会超时)。如果超过时间限制没有完成,则自动回滚
readOnly指定事务是否只读,默认为false
rollbackFor用于指定能够触发事务回滚的异常类型,并且可以指定多个异常类型

@Transaction事务注解原理

@Transaction的工作机制是基于AOP实现的,AOP有事使用动态代理实现的。如果目标对象实现了接口,默认情况下采用JDK的动态代理,如果对象没有实现接口,会使用Cglib来做动态代理

createAopProxy()方法决定了使用JDK还是Cglib来做动态代理

如果一个类或者一个类中的public方法上被标注@Transaction注解的话,Spring容器会在启动的时候为其创建一个代理类,在调用被@Transaction注解的public方法的时候,实际调用的是TransactionInterceptor类中的invoke方法。这个方法的作用就是在目标方法之前开启事务,方法执行过程中如果遇到异常的时候回滚事务,方法调用完成后提交事务。

注解失效情况

应用在非public修饰的方法上

Spring AOP自调用问题

若同一类中的其他没有@Transaction注解的方法内部调用有@Transaction注解的方法,有@Transaction注解的方法的事务会失效。

这是由于Spring AOP代理的原因造成的,因为只有@Transaction注解的方法在类以外被调用的时候,Spring事务管理才生效。

解决方法就是避免同一类中自调用或者使用AspectJ取代Spring AOP代理。

propagation属性设置错误

rollbackFor属性设置错误

try/catch中没有抛出异常导致失效

数据库不支持事务

最后

  • 如果觉得看完有收获,希望能给我点个赞,这将会是我更新的最大动力,感谢各位的支持

  • 欢迎各位关注我的公众号【java冢狐】,专注于java和计算机基础知识,保证让你看完有所收获,不信你打我

  • 如果看完有不同的意见或者建议,欢迎多多评论一起交流。感谢各位的支持以及厚爱。

——我是冢狐,和你一样热爱编程。

image

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多