目录
前言
1、Spring(Spring Boot)实现事务
1.1、通过代码的方式手动实现事务
1.2、通过注解@Transactional的方式实现声明式事务
1.2.1、实现:
1.2.2、程序中有try-catch时,程序发生异常事务不会回滚
解决方案一:将异常抛出去
解决方案二:使用代码手动回滚事务
1.2.3、@Transactional参数说明
1.2.4、@Transactional工作原理
2、事务隔离级别
2.1、事务特性
2.2、Spring中设置事务隔离级别
2.2.1、MySQL中事务隔离级别(4种)
2.2.2、Spring事务隔离级别(5种)
3、Spring事务传播机制
3.1、事务传播机制是什么
3.2、为什么需要事务传播机制
3.3、事务传播机制有哪些
3.4、事务传播的分类
3.5、Spring事务传播机制使用场景演示
3.5.1、支持当前事务(REQUIRED)
3.5.2、不支持当前事务(REQUIRES_NEW)
3.5.3、嵌套事务(NESTED)
3.6、嵌套事务和加入事务有什么区别?
为什么需要事务呢?首先需要了解什么是事务,事务就是一组操作封装成一个单元,执行时要么全部成功,要么全部失败~
就好比,你给我转账,你那边的金额减少了,我这边突然出故障了,钱就凭空没了,你当然是第一个不乐意呀~ 所以需要事务,当我这边失败时,事务回滚,你那边的金额就会回到转账前的金额~
Spring手动操作事务就和MySQL操作事务类似,包括三个步骤:
SpringBoot内置了两个对象:
代码:
package com.example.demo.controller;
import com.example.demo.entity.Userinfo;
import com.example.demo.service.UserService;
import org.apache.catalina.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.time.LocalDateTime;
/**
* Created with IntelliJ IDEA.
* Description:
* User:龙宝
* Date:2023-03-30
* Time:17:07
*/
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@Autowired
private DataSourceTransactionManager transactionManager;
@Autowired
private TransactionDefinition transactionDefinition;
@RequestMapping("/add")
public int add(Userinfo userinfo) {
//非空校验
if(userinfo == null || !StringUtils.hasLength(userinfo.getUsername())
|| !StringUtils.hasLength(userinfo.getPassword())) {
return 0;
}
//1、开启事务
TransactionStatus transactionStatus = transactionManager.getTransaction(transactionDefinition);
int result = userService.add(userinfo);
System.out.println("添加:" +result);
//2、回滚事务
// transactionManager.rollback(transactionStatus);
//3、提交事务
transactionManager.commit(transactionStatus);
return result;
}
}
@Transactional的特点:
代码;
@RequestMapping("/add")
@Transactional //声明式事务(自动提交)
public Integer add(Userinfo userinfo) {
//非空校验
if(userinfo == null || !StringUtils.hasLength(userinfo.getUsername()) || !StringUtils.hasLength(userinfo.getPassword())) {
return 0;
}
int result = userService.add(userinfo);
System.out.println("添加成功!" + result);
return result;
}
添加一个异常;
程序启动,前端:
数据库:
但此时,我们如果将这个异常捕捉掉,事务就不会回滚了~
程序启动,前端:
数据库:
不会回滚~
参数 | 作用 |
value | 当配置了多个事务管理器时,可以使用该属性指定选择哪个事务管理器 |
transactionManager | 当配置了多个事务管理器时,可以使用该属性指定选择哪个事务管理器 |
propagation | 事务传播行为【事务传播机制是,下文中有详解】 |
isolation | 事务的隔离级别,默认值为Isolation.DEFAULT |
timeout | 事务的超时时间,默认值为-1【无时间限制】。如果超过该时长但事务还没有完成,则自动进行回滚事务 |
readOnly | 指定事务是否为只读事务,默认值为false;为了忽略那些不需要事务的方法,比如读取数据可以设置read-only为true |
rollbackFor | 用户指定能够触发事务回滚的异常类型,可以指定多个异常类型 |
rollbackForClassName | 用于指定能够触发事务回滚的异常类型,可以指定多个异常类型 |
noRollbackFor | 抛出指定的异常类型,不回滚事务,也可以指定多个异常类型 |
noRollbackForClassName | 抛出指定的异常类型,不回滚事务,也可以指定多个异常类型 |
@Transactional是基于AOP实现的,AOP是使用动态代理实现的。如果目标对象实现了接口,默认情况下会采用JDK的动态代理,如果目标对象没有实现了接口,会使用CGLIB动态代理
@Transactional在开始执行业务之前,通过代理先开启事务,在执行之后再提交事务。如果中途遇到异常,则回滚事务
@Transactional实现思路:
4大特性:原子性、持久性、一致性、隔离性~
具体这篇文章下有详细说明:http://t.csdn.cn/fIugI
这篇文章中有详细说明:http://t.csdn.cn/fIugI
Spring事务隔离级别,4种和MySQL一致,另外多了一种:
Isolation.DEFAULT:以连接的数据库的隔离级别为主
Spring中如何设置事务隔离级别:
Spring事务中传播机制定义了多个包含了事务的方法,相互调用时,事务是如何在这些方法间进行传递的
事务隔离级别是保证多个并发事务执行的可控性的(稳定性),而事务传播机制是保证一个事务在多个调用方法间的可控性(稳定性)
事务隔离解决的是多个事务同时调用一个数据库的问题,如图:
事务传播机制解决的是一个事务在多个节点(方法)中的传递问题,如下:
Spring事务的传播机制有以下7种:
事务传播分为3类:
我们先开启事务,然后成功插入一条数据,然后执行日志时,报错,观察结果:
UserController:
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@Autowired
private LogService logService;
@RequestMapping("/add")
@Transactional(propagation = Propagation.REQUIRED)
public int add(Userinfo userinfo) {
//非空校验
if(userinfo == null || !StringUtils.hasLength(userinfo.getUsername()) || !StringUtils.hasLength(userinfo.getPassword())) {
return 0;
}
//插入数据
int result = userService.add(userinfo);
//插入日志
logService.saveLog("日志内容~");
return result;
}
}
UserService:
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public int add(Userinfo userinfo) {
return userMapper.add(userinfo);
}
}
LogService:
@Service
public class LogService {
@Transactional(propagation = Propagation.REQUIRED)
public void saveLog(String content) {
int i= 1/0;
System.out.println(content);
}
}
程序运行:
报异常 ,查看数据库,看事务是否回滚:
回滚了~
上述的UserController代码中将事务的传播机制更改为REQUIRES_NEW,将LogService中添加日志的方法前的事务传播机制更改为REQUIRES_NEW,代码更改:
@Service
public class LogService {
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveLog(String content) {
try {
int i = 1 / 0;
} catch (Exception e) {
//手动回滚当前事务
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
System.out.println(content);
}
}
程序执行:
前端:
查看数据库:
Userinfo表中成功插入数据,Log执行失败,但不影响UserController中的事务
UserController:
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@Autowired
private LogService logService;
@RequestMapping("/add")
@Transactional(propagation = Propagation.NESTED)
public int add(Userinfo userinfo) {
//非空校验
if(userinfo == null || !StringUtils.hasLength(userinfo.getUsername()) || !StringUtils.hasLength(userinfo.getPassword())) {
return 0;
}
//插入数据
int result1 = userService.add(userinfo);
int result2 = userService.insert();
return result1 + result2;
}
}
UserSevice:
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public int add(Userinfo userinfo) {
return userMapper.add(userinfo);
}
@Transactional(propagation = Propagation.NESTED)
public int insert() {
Userinfo userinfo = new Userinfo();
userinfo.setUsername("李四");
userinfo.setPassword("789");
int ret = userMapper.add(userinfo);
int i = 1/0;
return ret;
}
}
程序启动:
数据库:
结果:无数据添加
因为是嵌套事务,所以日志添加回滚之后,往上找调用它的方法和事务回滚了用户添加,所以用户数据添加都失败了
好啦,本期到这里就结束咯,我们下期见~