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 中通常可以通过以下两种方式来实现对事务的管理:
- 使用 Spring 的事务注解管理事务
- 使用 AspectJ 的 AOP 配置管理事务
事务管理 API
Spring 的事务管理,主要用到两个事务相关的接口。
事务管理器接口
事务管理器是 PlatformTransactionManager 接口对象。其主要用于完成事务的提交、回滚,及获取事务的状态信息。
常用的两个实现类
PlatformTransactionManager 接口有两个常用的实现类:
- DataSourceTransactionManager:使用 JDBC 或 MyBatis 进行数据库操作时使用。
- 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。
- READ_UNCOMMITTED: 读未提交。未解决任何并发问题。
- READ_COMMITTED: 读已提交。解决脏读,存在不可重复读与幻读。
- REPEATABLE_READ: 可重复读。解决脏读、不可重复读,存在幻读。
- 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()方法会创建一个事务,并在其中执行。
PROPAGATION_SUPPORTS:
指定的方法支持当前事务,但若当前没有事务,也可以以非事务方式执行。
PROPAGATION_REQUIRES_NEW :
总是新建一个事务,若当前存在事务,就将当前事务挂起,直到新事务执行完毕。
定义了默认事务超时时限
常量 TIMEOUT_DEFAULT 定义了事务底层默认的超时时限, sql 语句的执行时长。注意:事务的超时时限起作用的条件比较多(例如服务器与用户之间的网速,sql语句执行时长…),且超时的时间计算点较复杂。所以,该值一般就使用默认值即可。
使用Spring事务注解管理事务
通过@Transactional 注解方式, 可将事务织入到相应 public 方法中,实现事务管理。
@Transactional 的所有可选属性如下所示:
- propagation: 用于设置事务传播属性。该属性类型为 Propagation 枚举,默认值为Propagation.REQUIRED。
- isolation: 用于设置事务的隔离级别。该属性类型为 Isolation 枚举,默认值为
Isolation.DEFAULT。 - readOnly: 用于设置该方法对数据库的操作是否是只读的。该属性为 boolean,默认值为 false。
- timeout: 用于设置本操作与数据库连接的超时时限。单位为秒,类型为 int,默认值为-1,即没有时限。
- rollbackFor: 指定需要回滚的异常类。类型为 Class[],默认值为空数组。当然,若只有一个异常类时,可以不使用数组
- rollbackForClassName: 指定需要回滚的异常类类名。类型为 String[],默认值为空数组。当然,若只有一个异常类时,可以不使用数组。
- noRollbackFor: 指定不需要回滚的异常类。类型为 Class[],默认值为空数组。当然,若只有一个异常类时,可以不使用数组。
- noRollbackForClassName: 指定不需要回滚的异常类类名。类型为 String[],默认值为空数组。当然,若只有一个异常类时,可以不使用数组。
需要注意的是, @Transactional 若用在方法上,只能用于 public 方法上。对于其他非 public方法,如果加上了注解@Transactional, 虽然 Spring 不会报错,但不会将指定事务织入到该方法中。因为 Spring 会忽略掉所有非 public 方法上的@Transaction 注解。若@Transaction 注解在类上,则表示该类上所有的方法均将在执行时织入事务。
详细笔记见:Spring中LearnSpring12-Spring-trans-annotation模块。
使用AspectJ的AOP配置管理事务
详细笔记见:Spring中LearnSpring13-Spring-trans-aspectj模块。