1 前言
我们都知道,Spring 中的事务管理有编程式和声明式两种方式。其中,编程式事务是通过编码方式实现事务的,而声明式事务基于 AOP。由于声明式事务将具体业务逻辑与事务处理解耦,故其的应用范围较广。本文将介绍基于 @Transactional 注解的声明式事务实现形式。
2 注意事项
- 在默认配置的情况下,Spring 只会回滚继承自 RuntimeException 的异常或者 Error。
- 只有应用到 public 方法,@Transactional 注解才会生效。
这里我简单介绍一下 Java 的异常分类。其中 Throwable 是所有异常的根,而 Throwable 又可以分为 Error 和 Exception 两类。另外,Exception 内部也可分为两类,一为 Checked Exception,即可检查的异常,也称编译异常,这种我们必须使用 try-catch 去处理,另一类为 RuntimeException,即运行时异常,这种异常由虚拟机接管,我们可以不用处理。
3 最简单的使用
想要使用 Spring Boot 中的事务管理,只需在方法上加上 @Transactional 注解。
package edu.szu.test;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import edu.szu.test.entity.Book;
import edu.szu.test.service.BookService;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {TestApplication.class})
public class MyTest {
@Autowired
BookService bookService;
@Test
public void test(){
Book book = new Book();
book.setName("Tony1");
book.setPrice((float)13);
bookService.insert(book);
int i = 1/0;
}
}
我们先不使用 @Transactional 注解,随意插入一条数据,可以看到虽然后面我们程序出现了一个错误,但前面的数据还是已经插入了数据库。
现在我们尝试实现 Spring Boot 中的事务管理,在方法前加上 @Transactional 注解,看看数据是否还会插入数据库?
package edu.szu.test;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.transaction.annotation.Transactional;
import edu.szu.test.entity.Book;
import edu.szu.test.service.BookService;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {TestApplication.class})
public class MyTest {
@Autowired
BookService bookService;
@Test
@Transactional
public void test(){
Book book = new Book();
book.setName("Tony2");
book.setPrice((float)14);
bookService.insert(book);
int i = 1/0;
}
}
我们发现,加入 @Transactional 注解后,在程序抛出异常之后,事务会自动回滚,我们实现了 Spring Boot 中最简单的事务管理。
4 @Transactional 注解部分属性介绍
value/transactionManager
当配置了多个事务管理器时,可以使用 value 属性或者 transactionManager 属性指定选择哪个事务管理器。不管是 JPA 还是 JDBC 等都实现自接口 PlatformTransactionManager。如果添加的是 spring-boot-starter-jdbc 依赖,框架会默认注入 DataSourceTransactionManager 实例。如果添加的是 spring-boot-starter-data-jpa 依赖,框架会默认注入 JpaTransactionManager 实例。
propagation
该属性控制事务的传播行为,其默认值为 Propagation.REQUIRED
可选值如下
- PROPAGATION_REQUIRED:表示当前方法必须运行在事务中。如果当前事务存在,方法将会在该事务中运行。否则,会启动一个新的事务
- PROPAGATION_SUPPORTS:表示当前方法不需要事务上下文,但是如果存在当前事务的话,那么该方法会在这个事务中运行
- PROPAGATION_MANDATORY:表示该方法必须在事务中运行,如果当前事务不存在,则会抛出一个异常
- PROPAGATION_REQUIRED_NEW:表示当前方法必须运行在它自己的事务中。一个新的事务将被启动。如果存在当前事务,在该方法执行期间,当前事务会被挂起。如果使用JTATransactionManager的话,则需要访问TransactionManager
- PROPAGATION_NOT_SUPPORTED:表示该方法不应该运行在事务中。如果存在当前事务,在该方法运行期间,当前事务将被挂起。如果使用JTATransactionManager的话,则需要访问TransactionManager
- PROPAGATION_NEVER:表示当前方法不应该运行在事务上下文中。如果当前正有一个事务在运行,则会抛出异常
- PROPAGATION_NESTED:表示如果当前已经存在一个事务,那么该方法将会在嵌套事务中运行。嵌套的事务可以独立于当前事务进行单独地提交或回滚。如果当前事务不存在,那么其行为与PROPAGATION_REQUIRED一样。注意各厂商对这种传播行为的支持是有所差异的。可以参考资源管理器的文档来确认它们是否支持嵌套事务
isolation
即事务的隔离级别,默认值为 Isolation.DEFAULT。
- ISOLATION_DEFAULT 使用后端数据库默认的隔离级别
- ISOLATION_READ_UNCOMMITTED 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读
- ISOLATION_READ_COMMITTED 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生
- ISOLATION_REPEATABLE_READ 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生
- ISOLATION_SERIALIZABLE 最高的隔离级别,完全服从ACID的隔离级别,确保阻止脏读、不可重复读以及幻读,也是最慢的事务隔离级别,因为它通常是通过完全锁定事务相关的数据库表来实现的
timeout
事务的超时时间,默认值为-1。如果超过该时间限制但事务还没有完成,则自动回滚事务。
readOnly
指定事务是否为只读事务,默认值为 false。为了忽略那些不需要事务的方法,比如读取数据,可以设置 read-only 为 true。
rollbackFor
用于指定能够触发事务回滚的异常类型,可以指定多个异常类型。
noRollbackFor
抛出指定的异常类型,不回滚事务,也可以指定多个异常类型。