玩命加载中 . . .

Spring事务(04)


Spring事务

Maven相关依赖

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.xrebirth</groupId>
  <artifactId>LearnSpring12-Spring-trans-annotation</artifactId>
  <version>1.0-SNAPSHOT</version>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>11</maven.compiler.source>
    <maven.compiler.target>11</maven.compiler.target>
  </properties>

  <dependencies>
    <!--单元测试-->
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.13.2</version>
      <scope>test</scope>
    </dependency>
    <!--Spring核心依赖-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.3.14</version>
    </dependency>
    <!--
    spring-tx(事务包)为JDBC、Hibernate、JDO、JPA、Beans等提供的一致的声明式和编程式事务管理支持。
    -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-tx</artifactId>
      <version>5.3.14</version>
    </dependency>
    <!--
    spring-jdbc这个依赖包含对Spring对JDBC 数据访问进行封装的所有类。
    -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>5.3.14</version>
    </dependency>
    <!--JDBC驱动-->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>8.0.20</version>
    </dependency>
    <!--Mybatis依赖-->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.5.1</version>
    </dependency>
    <!--MyBaits和Spring集成的依赖-->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis-spring</artifactId>
      <version>2.0.6</version>
    </dependency>
    <!--阿里druid数据库连接池-->
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>1.2.8</version>
    </dependency>
  </dependencies>

  <build>
    <!--目的是把src/main/java目录中的所有包含xml的文件输出到classes目录中-->
    <resources>
      <resource>
        <!--需要不被过滤的目录-->
        <directory>src/main/resources</directory>
        <includes>
          <!--需要不被过滤的文件-->
          <include>**/*.properties</include>
          <include>**/*.xml</include>
        </includes>
        <!--filtering:是否启用过滤器:true(启用)/false(禁用)-->
        <filtering>true</filtering>
      </resource>
      <resource>
        <!--需要不被过滤的目录-->
        <directory>src/main/java</directory>
        <includes>
          <!--需要不被过滤的文件-->
          <include>**/*.properties</include>
          <include>**/*.xml</include>
        </includes>
        <!--filtering:是否启用过滤器:true(启用)/false(禁用)-->
        <filtering>true</filtering>
      </resource>
    </resources>
    <!-- 指定maven编译的jdk版本,如果不指定,maven3默认用jdk 1.5 maven2默认用jdk1.3 -->
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.8.0</version>
        <configuration>
          <!-- 同【方式一】的 maven.compiler.target -->
          <source>11</source>
          <!-- 同【方式一】的 maven.compiler.source -->
          <target>11</target>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>

事务管理

事务原本是数据库中的概念,在 Dao 层。但一般情况下,需要将事务提升到业务层,即 Service 层。这样做是为了能够使用事务的特性来管理具体的业务。 在 Spring 中通常可以通过以下两种方式来实现对事务的管理:

  1. 使用 Spring 的事务注解管理事务
  2. 使用 AspectJ 的 AOP 配置管理事务

事务管理 API

Spring 的事务管理,主要用到两个事务相关的接口。

事务管理器接口

事务管理器是 PlatformTransactionManager 接口对象。其主要用于完成事务的提交、回滚,及获取事务的状态信息。

事务管理器接口

常用的两个实现类

PlatformTransactionManager 接口有两个常用的实现类:

  1. DataSourceTransactionManager:使用 JDBC 或 MyBatis 进行数据库操作时使用。
  2. HibernateTransactionManager:使用 Hibernate 进行持久化数据时使用。

Spring 的回滚方式

Spring 事务的默认回滚方式是: 发生运行时异常和 error 时回滚,发生受查(编译)异常时提交。 不过,对于受查异常,开发人员也可以手工设置其回滚方式。

回顾错误与异常

Throwable 类是 Java 语言中所有错误或异常的超类。只有当对象是此类(或其子类之一)的实例时, 才能通过 Java 虚拟机或者 Java 的 throw 语句抛出。

Error 是程序在运行过程中出现的无法处理的错误,比如 OutOfMemoryError、ThreadDeath、 NoSuchMethodError 等。当这些错误发生时,程序是无法处理(捕获或抛出)的, JVM 一般会终止线程。

程序在编译和运行时出现的另一类错误称之为异常,它是 JVM 通知程序员的一种方式。通过这种方式,让程序员知道已经或可能出现错误,要求程序员对其进行处理。

异常分为运行时异常与受查异常。

运行时异常,是 RuntimeException 类或其子类, 即只有在运行时才出现的异常。如,NullPointerException、 ArrayIndexOutOfBoundsException、 IllegalArgumentException 等均属于运行时异常。这些异常由 JVM 抛出,在编译时不要求必须处理(捕获或抛出)。但只要代码编写足够仔细,程序足够健壮,运行时异常是可以避免的。

受查异常,也叫编译时异常,即在代码编写时要求必须捕获或抛出的异常,若不处理,则无法通过编译。如 SQLException, ClassNotFoundException, IOException 等都属于受查异常。

RuntimeException 及其子类以外的异常,均属于受查异常。当然,用户自定义的 Exception的子类,即用户自定义的异常也属受查异常。程序员在定义异常时,只要未明确声明定义的为 RuntimeException 的子类,那么定义的就是受查异常。

事务定义接口

事务定义接口 TransactionDefinition 中定义了事务描述相关的三类常量:事务隔离级别、事务传播行为、事务默认超时时限,及对它们的操作。

五个事务隔离级别常量

这些常量均是以 ISOLATION_开头。即形如 ISOLATION_XXX。

DEFAULT: 采用 DB 默认的事务隔离级别。 MySql 的默认为 REPEATABLE_READ; Oracle默认为 READ_COMMITTED。

  1. READ_UNCOMMITTED: 读未提交。未解决任何并发问题。
  2. READ_COMMITTED: 读已提交。解决脏读,存在不可重复读与幻读。
  3. REPEATABLE_READ: 可重复读。解决脏读、不可重复读,存在幻读。
  4. SERIALIZABLE: 串行化。不存在并发问题。

七个事务传播行为常量

所谓事务传播行为是指,处于不同事务中的方法在相互调用时,执行期间事务的维护情况。如, A 事务中的方法 doSome()调用 B 事务中的方法 doOther(),在调用执行期间事务的维护情况,就称为事务传播行为。事务传播行为是加在方法上的。

事务传播行为常量都是以 PROPAGATION_ 开头,形如 PROPAGATION_XXX。

PROPAGATION_REQUIRED
PROPAGATION_REQUIRES_NEW
PROPAGATION_SUPPORTS
PROPAGATION_MANDATORY
PROPAGATION_NESTED
PROPAGATION_NEVER
PROPAGATION_NOT_SUPPORTED

PROPAGATION_REQUIRED:

指定的方法必须在事务内执行。若当前存在事务,就加入到当前事务中;若当前没有事务,则创建一个新事务。这种传播行为是最常见的选择,也是 Spring 默认的事务传播行为。 如该传播行为加在 doOther()方法上。若 doSome()方法在调用 doOther()方法时就是在事
务内运行的,则 doOther()方法的执行也加入到该事务内执行。若 doSome()方法在调用doOther()方法时没有在事务内执行,则 doOther()方法会创建一个事务,并在其中执行。

image-20220112173746326

PROPAGATION_SUPPORTS:

指定的方法支持当前事务,但若当前没有事务,也可以以非事务方式执行。

image-20220112173825526

PROPAGATION_REQUIRES_NEW :

总是新建一个事务,若当前存在事务,就将当前事务挂起,直到新事务执行完毕。

image-20220112173855053

定义了默认事务超时时限

常量 TIMEOUT_DEFAULT 定义了事务底层默认的超时时限, sql 语句的执行时长。注意:事务的超时时限起作用的条件比较多(例如服务器与用户之间的网速,sql语句执行时长…),且超时的时间计算点较复杂。所以,该值一般就使用默认值即可。

使用Spring事务注解管理事务

通过@Transactional 注解方式, 可将事务织入到相应 public 方法中,实现事务管理。

@Transactional 的所有可选属性如下所示:

  1. propagation: 用于设置事务传播属性。该属性类型为 Propagation 枚举,默认值为Propagation.REQUIRED。
  2. isolation: 用于设置事务的隔离级别。该属性类型为 Isolation 枚举,默认值为
    Isolation.DEFAULT。
  3. readOnly: 用于设置该方法对数据库的操作是否是只读的。该属性为 boolean,默认值为 false。
  4. timeout: 用于设置本操作与数据库连接的超时时限。单位为秒,类型为 int,默认值为-1,即没有时限。
  5. rollbackFor: 指定需要回滚的异常类。类型为 Class[],默认值为空数组。当然,若只有一个异常类时,可以不使用数组
  6. rollbackForClassName: 指定需要回滚的异常类类名。类型为 String[],默认值为空数组。当然,若只有一个异常类时,可以不使用数组。
  7. noRollbackFor: 指定不需要回滚的异常类。类型为 Class[],默认值为空数组。当然,若只有一个异常类时,可以不使用数组。
  8. noRollbackForClassName: 指定不需要回滚的异常类类名。类型为 String[],默认值为空数组。当然,若只有一个异常类时,可以不使用数组。

需要注意的是, @Transactional 若用在方法上,只能用于 public 方法上。对于其他非 public方法,如果加上了注解@Transactional, 虽然 Spring 不会报错,但不会将指定事务织入到该方法中。因为 Spring 会忽略掉所有非 public 方法上的@Transaction 注解。若@Transaction 注解在类上,则表示该类上所有的方法均将在执行时织入事务。

详细笔记见:Spring中LearnSpring12-Spring-trans-annotation模块。

使用AspectJ的AOP配置管理事务

详细笔记见:Spring中LearnSpring13-Spring-trans-aspectj模块。


文章作者: 小靳同学
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 小靳同学 !
评论
  目录