前情提要
飞书的文档不好转移,可以直接看我的飞书:Docs
什么是事务
是一组操作的集合,是一个不可分割的操作
事物会将所有操作当作一个整体,同时对数据库进行操作请求,这些操作要么全部成功,要么全部失败
总会有一些操作,需要同步进行,这个时候就需要使用事务
数据库中,自增类型的字段,一旦被分配了数据,就是不会进行回滚的,尽管整个数据进行了回滚(或者数据没有成功进行添加),但是该字段还是进行了自增
操作步骤
-
开启事 start transaction/begin(操作前开启事务)
-
提交事务: commit(操作全部成功,提交事务)
-
回滚事务: rollback(操作中只要有一个出现异常,就回滚事务)
– 开启事务
start transaction;– 提交任务
commit;– 回滚事务
rollback;
事务的实现
分类(Spring):
-
编程式事务(手动代码)
-
声明式事务(利用注解自动)
编程式事务(了解)
添加依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
</dependency>
定义对象.使用@Autowired,都是Spring帮忙处理过的
-
DataSourceTransactionManager 生成的事务管理器
-
TransactionDefinition 需要注入的对象
对应的start操作:
TransactionStatus transactionStatus = DataSourceTransactionManager.getTransaction(transactionDefinition);
回滚
DataSourceTransactionManager.rollback(transactionStatus);
代码示例
@Slf4j
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private DataSourceTransactionManager TransactionManager;//事务管理器
@Autowired
private TransactionDefinition transactionDefinition;//需要注入的参数
@Autowired
private UserService userService;
@RequestMapping("/registry")
public Boolean registry(String userName,String password){
TransactionStatus transactionStatus = TransactionManager.getTransaction(transactionDefinition);//设定了开始状态
Integer result = userService.insertInfo(userName,password);
log.info("插入信息: "+result);
TransactionManager.rollback(transactionStatus);//回滚
return true;
}
}
@Service
public class UserService {
@Autowired
private UserInfoMapper userInfoMapper;
public Integer insertInfo(String userName, String password) {
return userInfoMapper.insertInfo(userName,password);
}
}
@Mapper
public interface UserInfoMapper {
@Insert("insert into user_info(`user_name`,`password`) values (#{userName},#{password})")
Integer insertInfo(@Param("userName") String userName, @Param("password") String password);
}
声明式事务 @Transactional (主要使用方法!!!)
对于 @Transactional 注解,可以使用在类上(对类中的方法均生效),也可以使用在方法上(只对该方法生效)
加入注解之后,会自动开启事务,自动判断执行commit或者rollback操作,但是这个注解只会对 public 方法生效
代码类型
-
提交成功
@Transactional
@RequestMapping(“/registry”)
public Boolean registry(String userName,String password){
Integer result = userService.insertInfo(userName,password);
log.info(“影响的行数:”+result);
return true;
}
2.出现异常,回滚
没有出现 commit 语句
@Transactional
@RequestMapping("/registry2")
public Boolean registry2(String userName,String password){
Integer result = userService.insertInfo(userName,password);
log.info("影响的行数:"+result);
int i = 10/0;
return true;
}
3.捕获异常
将异常进行捕获之后,就会判断没有错误,此时就会正常进行事务提交
@Transactional
@RequestMapping("/registry3")
public Boolean registry3(String userName,String password){
Integer result = userService.insertInfo(userName,password);
log.info("影响的行数:"+result);
try {
int i = 10/0;
}catch (Exception e){
log.info("出现异常");
}
return true;
}
4.捕获异常后继续抛出
@Transactional
@RequestMapping("/registry4")
public Boolean registry4(String userName,String password){
Integer result = userService.insertInfo(userName,password);
log.info("影响的行数:"+result);
try {
int i = 10/0;
}catch (Exception e){
log.info("出现异常");
throw e;
}
return true;
}
5.手动设置回滚
手动设置之后,代码不会报错,但是还是会进行回滚
手动设置回滚代码: TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()
@Transactional
@RequestMapping("/registry5")
public Boolean registry5(String userName,String password){
Integer result = userService.insertInfo(userName,password);
log.info("影响的行数:"+result);
try {
int i = 10/0;
}catch (Exception e){
log.info("设置手动回滚");
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
return true;
}
6.手动设置其他异常回滚
在注解进行回滚类型修改,当抛出异常时,会将该异常当作设置异常进行处理,同时进行回滚,这样就避免了注解不会回滚其他类型异常的问题
@Transactional(rollbackFor = Exception.class,isolation = Isolation.DEFAULT)
@RequestMapping("/registry6")
public Boolean registry6(String userName,String password) throws IOException {
Integer result = userService.insertInfo(userName,password);
log.info("影响的行数:"+result);
if(true){
throw new IOException();
}
return true;
}
回滚类型:
-
运行时异常(其他类型的异常是不会回滚的)–>RuntimeException
-
ERROR
其他异常的回滚需要进行单独设置
异常处理
对于异常进行捕获,没有抛出(内部解决),此时会认为没有报错,还是会提交!!
但是捕获后,重新抛出,还是会回滚
同时,捕获后可以设置手动回滚,此时事务还是会回滚
事务的隔离级别
级别分类
读未提交
该隔离级别的事务可以看到其他事务中未提交的数据,将他发生的问题叫做脏读(还没有进行提交,其他事务就已经读取到了,但是可能数据会回滚)
读提交
该隔离级别的事务可以读取到已经提交事务的数据(在不同的时间的相同SQL查询可能会有不同的结果) 问题:不可重复度
可重复读
不会读到其他事务对已有数据的修改,确保同一事务多次查询的结果是一致的,是MySQL的默认事务隔离级别
串行化
序列化,事务最高隔离级别,会强制事务排序,不会脏读,不可重复读和幻读,但是效率低
图解(AB都是同一个表)
读已提交(不可重复读)
可重复读
事务传播机制!(MsSQL里面不涉及,但是Java涉及)
场景一:当方法A设置了事务注解的同时,还调用了方法B,但是方法B也是设置了事务注解,此时如果回滚,是按照方法A还是方法B呢 --> 当B发生异常需要回滚,此时A需要回滚吗
场景二:当方法A设置了事务注解的同时,还调用了方法B,但是方法B也是设置了事务注解,此时方法B是加入A的事务还是创建新事务呢
传播机制!
解析(全部建立在B设定了**@Transactional(propagation=)**的前提下):
不创建新事务部分:
-
如果A有事务,B就加入,此时共用一个事务,二者之间只要任意一个出现问题,就全部回滚;如果A没有事务,B就创建事务(这个是默认的)
-
如果A有事务,B就加入;如果A没有事务,B就以没有事务的状态继续运行(此时就是普通调用)
-
如果A存在事务,B就加入;如果A没有,B就会抛出异常(强制性)
创建新事务部分:
-
不管A是否有事务,B都是会创建一个新事务
-
不管A是否有事务,B都还是以没有事务的状态运行
-
如果A有事务,B就抛出异常;如果A没有事务,B就以没有事务的状态继续运行
-
如果A有事务,B就会创建一个事务,作为A的事务的嵌套事务;如果A没有事务,B就会像第一条
场景演示
代码准备
-
Controller
@Slf4j
@RestController
@RequestMapping(“trans2”)
public class LogController {@Autowired private UserService userService; @Autowired private LogService logService; @Transactional @RequestMapping("/registry") public Boolean registry(String userName,String password){ Integer result = userService.insertInfo(userName,password); log.info("表一影响的行数:"+result); Integer result1 = logService.insertInfo(userName,"registry"); log.info("表二影响的行数:"+result1); return true; }
}
2.表1
@Service
public class UserService {
@Autowired
private UserInfoMapper userInfoMapper;
public Integer insertInfo(String userName, String password) {
return userInfoMapper.insertInfo(userName,password);
}
}
@Mapper
public interface UserInfoMapper {
@Insert("insert into user_info(`user_name`,`password`) values (#{userName},#{password})")
Integer insertInfo(@Param("userName") String userName, @Param("password") String password);
}
-
表2
@Service
public class LogService {
@Autowired
private LogInfoMapper logInfoMapper;public Integer insertInfo(String userName,String op){ return logInfoMapper.insertInfo(userName,op); }
}
@Mapper
public interface LogInfoMapper {
@Insert(“insert into log_info(user_name
,op
) values (#{userName},#{op})”)
Integer insertInfo(@Param(“userName”) String userName, @Param(“op”) String op);
}
进行测试
- Propagation.REQUIRED
正常运行
代码(只显示修改部分)
@Transactional
@RequestMapping("/registry")
public Boolean registry(String userName,String password){
Integer result = userService.insertInfo(userName,password);
log.info("表一影响的行数:"+result);
Integer result1 = logService.insertInfo(userName,"registry");
log.info("表二影响的行数:"+result1);
return true;
}
@Transactional(propagation = Propagation.REQUIRED)
public Integer insertInfo(String userName, String password) {
return userInfoMapper.insertInfo(userName,password);
}
@Transactional(propagation = Propagation.REQUIRED)
public Integer insertInfo(String userName,String op){
return logInfoMapper.insertInfo(userName,op);
}
2.Propagation.SUPPORTS
取消Controller的事务注解,其他被调用的方法以普通方式运行
代码
@RequestMapping("/registry")
public Boolean registry(String userName,String password){
Integer result = userService.insertInfo(userName,password);
log.info("表一影响的行数:"+result);
Integer result1 = logService.insertInfo(userName,"registry");
log.info("表二影响的行数:"+result1);
return true;
}
@Transactional(propagation = Propagation.SUPPORTS)
public Integer insertInfo(String userName, String password) {
return userInfoMapper.insertInfo(userName,password);
}
@Transactional(propagation = Propagation.SUPPORTS)
public Integer insertInfo(String userName,String op){
return logInfoMapper.insertInfo(userName,op);
}
3.Propagation.MANDATORY
取消Controller的事务注解,此时报错
代码
@Transactional(propagation = Propagation.MANDATORY)
public Integer insertInfo(String userName, String password) {
return userInfoMapper.insertInfo(userName,password);
}
@Transactional(propagation = Propagation.MANDATORY)
public Integer insertInfo(String userName,String op){
return logInfoMapper.insertInfo(userName,op);
}
4.Propagation.REQUIRES_NEW
取消Controller的事务注解,此时调用的方法创建新事务
代码
@Transactional(propagation = Propagation.REQUIRES_NEW)
public Integer insertInfo(String userName, String password) {
return userInfoMapper.insertInfo(userName,password);
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public Integer insertInfo(String userName,String op){
return logInfoMapper.insertInfo(userName,op);
}
5.Propagation.NOT_SUPPORTED
由Controller开启事务
代码
@Transactional
@RequestMapping("/registry")
public Boolean registry(String userName,String password){
Integer result = userService.insertInfo(userName,password);
log.info("表一影响的行数:"+result);
Integer result1 = logService.insertInfo(userName,"registry");
log.info("表二影响的行数:"+result1);
return true;
}
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public Integer insertInfo(String userName, String password) {
return userInfoMapper.insertInfo(userName,password);
}
6.Propagation.NEVER
Controler有事务
运行截图
Controller没有事务
运行截图
7. Propagation.NESTED
应该类似于连坐制,可以进行部分回滚
运行截图