文章目录
- 1. Spring 事务的实现
- 1.1 Spring 手动操作事务
- 1.2 Spring 声明式事务
- 1.2.1 @Transactional 作用范围
- 1.2.2 @Transactional 参数说明
- 1.2.3 异常被捕获, 不会事务回滚
- ① 解决办法 --- 将异常重新抛出
- ② 解决办法 --- 手动回滚事务
- 2. Spring 事务的隔离级别
- 2.1 Spring事务的隔离级别有 5 种
- 2.2 Spring 中设置事务隔离级别
- 3. Spring 事务的传播机制
- 3.1 事务传播机制有哪些?
- 3.2 代码演示
- 3.2.1 支持当前事务
- 3.2.2 不支持当前事务
- 3.2.3 不支持当前事务, NEVER 抛异常
- 3.2.4 嵌套事务
- 3.3 嵌套事务和加入事务的区别
- 3.3.1 嵌套事务
- 3.3.2 加入事务
- 3.3.3 总结
1. Spring 事务的实现
Spring 中事务的实现有两种方法:
- 手动操作事务
- 声明式自动提交事务
1.1 Spring 手动操作事务
Spring 手动操作事务, 一共三个重要步骤: ①开启事务,②提交事务,③回滚事务
@RestController
public class UserController2 {@Resourceprivate UserService userService;// JDBC 事务管理器@Resourceprivate DataSourceTransactionManager dataSourceTransactionManager;// 定义事务属性@Resourceprivate TransactionDefinition transactionDefinition;@RequestMapping("/save")public Object save(User user) {// 1. 开启事务TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);// 插入数据库int result = userService.save(user);// 2. 提交事务dataSourceTransactionManager.commit(transactionStatus);// 3. 回滚事务dataSourceTransactionManager.rollback(transactionStatus);return result;}
}
1.2 Spring 声明式事务
在方法上添加 @Transactional
注解就可以实现了
无需手动开启事务和提交事务, 进入方法时自动开启事务, 方法执行完会自动提交事务, 如果中途发生了没有处理的异常会自动回滚事务
@RestController
public class UserController2 {@Resourceprivate UserService userService;@RequestMapping("/save")@Transactionalpublic Object save(User user) {int result = userService.save(user);return result;}
}
1.2.1 @Transactional 作用范围
@Transactional 可以用来修饰方法或类:
- 修饰方法的时候, 需要注意只能应用到
public
方法上, 否则不生效.- 修饰类时, 表明该注解对该类中所有的
public
方法都生效.
1.2.2 @Transactional 参数说明
参数 | 作用 |
---|---|
value | 当配置了多个事务管理器时, 可以使用该属性指定选择哪个事务管理器 |
transactionManager | 当配置了多个事务管理器时, 可以使用该属性指定选择哪个事务管理器. |
propagation | 事务的传播行为. 默认为 Propagation.REQUIRED |
isolation | 事务的隔离级别. 默认为 Isolation.DAEFAULT |
timeout | 事务的超时事件. 默认值为-1, 如果超过该时间限制但事务还没有完成, 则自动回滚事务. |
readOnly | 指定事务是否只读事务. 默认为 false. 为了忽略那些不需要事务的方法, 比如读取数据, 可以设置为 read-only 为 true |
rollbackFor | 用于指定能够触发事务回滚的异常类型, 可以指定多个异常类型 |
rollbackForClassName | 用于指定能够触发事务回滚的异常类型, 可以指定多个异常类型 |
noRollbackFor | 抛出指定的异常类型, 不回滚事务, 也可以指定多个异常类型 |
noRollbackForClassName | 抛出指定的异常类型, 不回滚事务, 也可以指定多个异常类型 |
1.2.3 异常被捕获, 不会事务回滚
当前的代码:
@RequestMapping("/save")@Transactionalpublic Object save(User user) {int result = userService.save(user);try {int a = 10/0;}catch (Exception e) {e.printStackTrace();}return result;}
当前数据库表中的内容:
运行之后表中的内容
此时发现, 出现异常的情况, 不会发生事务回滚
① 解决办法 — 将异常重新抛出
@RequestMapping("/save")@Transactionalpublic Object save(User user) {int result = userService.save(user);try {int a = 10/0;}catch (Exception e) {e.printStackTrace();throw e;}return result;}
此时数据库中也不会有数据
② 解决办法 — 手动回滚事务
使用 TransctionAspectSupport.currentTransactionStatus() 可以得到当前事务
然后使用 setRollbackOnly 就可以实现回滚.
@RequestMapping("/save")@Transactionalpublic Object save(User user) {int result = userService.save(user);try {int a = 10/0;}catch (Exception e) {e.printStackTrace();TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();}return result;}
2. Spring 事务的隔离级别
2.1 Spring事务的隔离级别有 5 种
Isolation.DEFAULT
: 以连接的数据库的事务隔离级别为主Isolation.READ_UNCOMMITTED
: 读未提交, 可以读取到未提交的事务, 存在脏读Isolation.READ_COMMITTED
: 读已提交, 只能读取到已经提交的事务, 解决了脏读, 但存在不可重复读Isolation.REPEATABLE_READ
: 可重复读, 解决了不可重复读, 但存在幻读Isolation.SERIALIZABLE
: 串行化, 可以解决所有并发问题, 但性能低
2.2 Spring 中设置事务隔离级别
只需要通过 @Transactional 中的 isolation 属性进行设置即可
3. Spring 事务的传播机制
Spring 事务传播机制定义了多个包含了事务的方法, 相互调用时, 事务是如何在这些方法间进行传递的
事务隔离级别 保证多个并发事务执行的可控性
事务传播机制 保证一个事务在多个调用方法间的可控性
3.1 事务传播机制有哪些?
Spring 事务的传播机制包含以下 7 种 :
Propagation.REQUIRED
: 默认的事务传播级别, 它表示如果当前存在事务, 则加入该事务; 如果当前没有事务, 则创建一个新的事务.Propagation.SUPPORTS
: 如果当前存在事务, 则加入该事务; 如果当前没有事务, 则以非事务的方式继续运行.Propagation.MANDATORY
: 如果当前存在事务, 则加入该事务; 如果当前没有事务, 则抛出异常.Propagation.REQUIRES_NEW
: 表示创建一个新的事务, 如果当前存在事务, 则把当前事务挂起.Propagation.NOT_SUPPORTED
: 以非事务方式运行, 如果当前存在事务, 则把当前事务挂起.Propagation.NEVER
: 以非事务方式运行, 如果当前存在事务, 则抛出异常.Propagation.NESTED
: 如果当前存在事务, 则创建一个事务作为当前事务的嵌套事务来运行; 如果不存在事务, 则创建一个新的事务.
3.2 代码演示
3.2.1 支持当前事务
@RestController
public class UserController2 {@Resourceprivate UserService userService;@Resourceprivate LogService logService;@RequestMapping("/save")@Transactional(propagation = Propagation.REQUIRED)public Object save(User user) {int result = userService.save(user);logService.saveLog("用户插入: " + user.getUsername());return result;}
}
UserService 代码 和 LogService 代码
运行结果:
发现 LogSerivice 出错, 导致 LogService操作回滚, 也导致 UserService 报错
3.2.2 不支持当前事务
修改 UserService
和 LogService
为 Propagation.REQUIRES_NEW
运行结果:
User表中插入成功, Log表中插入失败.
3.2.3 不支持当前事务, NEVER 抛异常
修改 LogService
为 Propagation=Propagation.NEVER
将 log的代码移到前面
@RequestMapping("/save")@Transactionalpublic Object save(User user) {logService.saveLog("用户插入: " + user.getUsername());int result = userService.save(user);return result;}
运行结果:
执行到 logService.saveLog 这段就报错. 就不会继续往下走了.
3.2.4 嵌套事务
修改 LogService
和 UserService
代码为 Propagation.NESTED
运行结果:
用户表和日志表都没有添加任何的数据
3.3 嵌套事务和加入事务的区别
3.3.1 嵌套事务
将嵌套事务中的 LogService 中进行当前事务的回滚操作.
运行结果:
User表操作成功, Log表操作失败.
3.3.2 加入事务
还是在 LogService 中 执行回滚操作.
运行结果:
此时 User表插入数据失败, Log表插入数据也失败.
3.3.3 总结
嵌套事务 (NESTED) 和 加入事务 (REQUIRED) 的区别
- 整个事务如果全部执行成功, 二者的结果是一样的.
- 如果事务执行到一半失败了, 那么加入事务整个会全部回滚; 而嵌套事务会局部回滚, 不会影响上一个方法中执行的结果.