SpringBoot事务配置管理
文章目录
- 1.事务
- 2.SpringBoot事务配置
- 2.1 依赖导入
- 2.2 事务的测试
- 3.常见问题总结
- 3.1 异常并没有被捕获到
- 3.2 异常被“吃”掉
- 3.3 事务的范围
1.事务
使用功能场景:
由于数据操作在顺序执行的过程中,线上可能有各种无法预知的问题,任何一步操作都有可能发生异常,异常则会导致后续的操作无法完成,此时由于业务逻辑并未正确的完成,所以在之前操作数据库的动作并不可靠,需要在这种情况下进行数据的回滚。
事务的作用就是为了保证用户的每一个操作都是可靠的,事务中的每一步操作都必须成功执行,只要有发生异常就回退到事务未进行操作的状态。
事务管理是SpringBoot框架中最为常用的功能之一,我们在实际应用开发时,基本上在service层处理业务逻辑的时候都要加上事务,当然了,有时候可能由于场景需要,也不用加事务(比如我们往一个表里插数据,相互没有影响,插多少是多少,不能因为某个数据挂了,把之前插的全部回滚)
2.SpringBoot事务配置
2.1 依赖导入
在SpringBoot中使用事务,需要导入mybatis依赖:
<dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.1.0</version></dependency>
导入了mybatis依赖后,SpringBoot会自动注入DataSourceTransactionManager,我们不需要任何其他的配置就可以用@Transactional注解进行事务的使用,关于MyBatis的配置,上文已经说明,这里和上文Mybatis配置一致即可
2.2 事务的测试
我们首先在数据库表中插入一条数据
然后我们写一个插入的mapper:
package com.example.springdemo1.dao;import com.example.springdemo1.pojo.User;
import org.apache.ibatis.annotations.*;
import org.springframework.stereotype.Repository;@Mapper
@Repository
public interface UserMapper {@Insert("insert into user(id,name,pwd) values (#{id},#{name},#{pwd}")Integer insertUser(User user);
}
OK,接下来我们来测试一下SpringBoot中的事务处理,在service层,我们手动抛出个异常来模拟实际中出现的异常,然后观察一下事务有没有回滚,如果数据库中没有新的记录,则说明事务回滚成功
package com.example.springdemo1.service;import com.example.springdemo1.pojo.User;public interface UserService{void insertUser(User user);
}
package com.example.springdemo1.service.impl;import com.example.springdemo1.dao.UserMapper;
import com.example.springdemo1.pojo.User;
import com.example.springdemo1.service.UserService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;import javax.annotation.Resource;@Service
public class UserServiceImpl implements UserService{@Resourceprivate UserMapper userMapper;@Override@Transactionalpublic void insertUser(User user) {//插入用户信息userMapper.insertUser(user);//手动抛出异常throw new RuntimeException();}
}
我们来测试一下:
package com.example.springdemo1.controller;import com.example.springdemo1.pojo.User;
import com.example.springdemo1.service.UserService;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;@RestController
@RequestMapping("/test")
public class testController11 {@Resourceprivate UserService userService;@PostMapping("/addUser")public String addUser(@RequestBody User user){if(user != null){userService.insertUser(user);return "success";}else{return "failure";}}
}
我们使用postman调用一下该接口,因为在程序中抛出了个异常,会造成事务回滚,我们刷新一下数据库,并没有增加一条记录,说明事务生效了。
3.常见问题总结
3.1 异常并没有被捕获到
首先要说的,就是异常并没有被捕获到,导致事务并没有回滚,我们在业务层代码中,也许已经考虑到了异常的存在,或者编辑器已经提示我们需要抛出异常,但是这里面有个需要注意的地方:并不是说我们把异常抛出来了,有异常了事务就会回滚,我们来看一个例子:
package com.example.springdemo1.service;import com.example.springdemo1.pojo.User;public interface UserService{void insertUser2(User user) throws Exception;
}
package com.example.springdemo1.service.impl;import com.example.springdemo1.dao.UserMapper;
import com.example.springdemo1.pojo.User;
import com.example.springdemo1.service.UserService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;import javax.annotation.Resource;
import java.sql.SQLException;@Service
public class UserServiceImpl implements UserService{@Resourceprivate UserMapper userMapper;@Override@Transactionalpublic void insertUser2(User user) throws Exception {//插入用户信息userMapper.insertUser(user);//手动抛出异常throw new SQLException("数据库异常");}}
我们看上面这个代码,其实并没有什么问题,手动抛出一个SQLException来模拟实际中操作数据库发生的异常,在这个方法中,既然跑出了异常,那么事务应该回滚,实际却不如此,写个测试类测试一下
package com.example.springdemo1.controller;import com.example.springdemo1.pojo.User;
import com.example.springdemo1.service.UserService;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;@RestController
@RequestMapping("/test")
public class testController11 {@Resourceprivate UserService userService;//测试异常并没有被捕获到@PostMapping("/addUser2")public String adddUser2(@RequestBody User user) throws Exception{if(user != null){userService.insertUser2(user);return "success";}else{return "failure";}}
}
通过postman测试一下,就会发现,仍然是可以插入一条用户数据的,那么问题出在哪呢?因为SpringBoot默认的事务规则是遇到运行异常和程序错误才会回滚,比如上述例子中抛出的RuntimeException就没有问题,但是抛出SQLException就无法回滚了,针对非运行时异常,如果要进行事务回滚的话,可以在 @Transactional注解中使用rollbackFor属性来指定异常,比如@Transactional(rollbackFor = Exception.class)
,这样就没有问题了。
3.2 异常被“吃”掉
我们在处理异常时,有两种方式,要么抛出去,让上一层来捕获处理,要么把异常try catch掉,在异常出现的地方给处理掉,就因为有这种try catch,所以导致异常被“吃”掉,事务无法回滚
,我们还是看上面那个例子,简单修改一下代码:
package com.example.springdemo1.service;import com.example.springdemo1.pojo.User;public interface UserService{void insertUser3(User user);
}
package com.example.springdemo1.service.impl;import com.example.springdemo1.dao.UserMapper;
import com.example.springdemo1.pojo.User;
import com.example.springdemo1.service.UserService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;import javax.annotation.Resource;
import java.sql.SQLException;@Service
public class UserServiceImpl implements UserService{@Resourceprivate UserMapper userMapper;@Override@Transactional(rollbackFor = Exception.class)public void insertUser3(User user) {user = new User(7,"lyh7","123456");try{//插入用户信息userMapper.insertUser(user);//手动抛出异常throw new SQLException("数据库异常");}catch(Exception e){//异常处理逻辑}}
}
package com.example.springdemo1.controller;import com.example.springdemo1.pojo.User;
import com.example.springdemo1.service.UserService;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;@RestController
@RequestMapping("/test")
public class testController11 {@Resourceprivate UserService userService;//测试异常被吃掉@PostMapping("/addUser3")public String addUser3(@RequestBody User user){if (user != null){userService.insertUser3(user);return "success";}else{return "failure";}}
}
通过postman测试一下,就会发现,仍然是可以插入一条用户数据,说明事务并没有因为抛出异常而回滚,那这种怎么解决呢?直接往上抛,给上一层来处理即可,千万不要在事务中把异常自己“吃”掉。
3.3 事务的范围
举个例子:
package com.example.springdemo1.service.impl;import com.example.springdemo1.dao.UserMapper;
import com.example.springdemo1.pojo.User;
import com.example.springdemo1.service.UserService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;import javax.annotation.Resource;
import java.sql.SQLException;@Service
public class UserServiceImpl implements UserService{@Resourceprivate UserMapper userMapper;@Override@Transactional(rollbackFor = Exception.class)public synchronized void insertUser4(User user){//实际中的具体业务userMapper.insertUser(user);}
}
可以看到,因为要考虑并发问题,在业务层代码的方法上加了个synchronized关键字,从上面方法中可以看到,方法上是加了事务的,那么也就是说,在执行该方法开始时,事务启动,执行完了之后,事务关闭,但是synchronized没有起作用,其实根本原因是因为事务的范围比锁的范围大,也就是说,在加锁的那部分代码执行完之后,锁释放掉了,但是事务还没有结束,此时另一个线程进来了,事务没结束的话,第二个线程进来时,数据库的状态和第一个线程刚进来是一样的,即由于mysql InnoDB引擎的默认隔离级别是可重复读(在同一个事务里,select的结果是事务开始时时间点的状态),线程二事务开始的时候,线程一还没提交完成,导致读取的数据还没更新呢,第二个线程也做了插入动作,导致了脏数据。
这个问题可以避免:
- 把事务去掉即可
- 在调用该service的地方加锁,保证锁的范围比事务的范围大即可
SpringBoot事务配置管理相关推荐
- springboot 事务回滚
springboot 事务回滚 springboot 事务使用 springboot 事务使用 **1.**只有在开启事务的方法中出现异常,才会自动回滚,需要在service的public方法上面加上 ...
- springboot 事务统一配置_Spring Boot实现分布式微服务开发实战系列(五)
最近已经推出了好几篇SpringBoot+Dubbo+Redis+Kafka实现电商的文章,今天再次回到分布式微服务项目中来,在开始写今天的系列五文章之前,我先回顾下前面的内容. 系列(一):主要说了 ...
- springboot 事务手动回滚_来,讲讲Spring事务有哪些坑?
来自公众号:孤独烟 引言 今天,我们接上文<面试官:谈谈你对mysql事务的认识>的内容,来讲spring中和事务有关的考题! 因为事务这块,面试的出现几率很高.而大家工作中CRUD的比较 ...
- springboot事务回滚源码_Spring Boot中的事务是如何实现的
1. 概述 一直在用SpringBoot中的@Transactional来做事务管理,但是很少想过SpringBoot是如何实现事务管理的,今天从源码入手,看看@Transactional是如何实现事 ...
- springboot 事务_原创002 | 搭上SpringBoot事务源码分析专车
前言 如果这是你第二次看到师长,说明你在觊觎我的美色! 点赞+关注再看,养成习惯 没别的意思,就是需要你的窥屏^_^ 专车介绍 该趟专车是开往Spring Boot事务源码分析的专车 专车问题 为什么 ...
- 26.SpringBoot事务注解详解
转自:https://www.cnblogs.com/kesimin/p/9546225.html @Transactional spring 事务注解 1.简单开启事务管理 @EnableTrans ...
- springboot 事务手动回滚_Spring Boot中的事务是如何实现的
1. 概述 一直在用SpringBoot中的@Transactional来做事务管理,但是很少想过SpringBoot是如何实现事务管理的,今天从源码入手,看看@Transactional是如何实现事 ...
- springboot+事务,多张表的操作事务回滚
第一步,在springboot的启动类上开启事务,注解 @EnableTransactionManagement 第二步:事务注解,回滚 @Transactional(rollbackFor = Ex ...
- springboot 事务_Spring Boot中的事务是如何实现的?懂吗?
一个SpringBoot问题就干趴下了?我却凭着这份PDF文档吊打面试官. 金三银四第一天,啃透这些SpringBoot知识点,还怕干不赢面试官? Spring全家桶笔记:Spring+Spring ...
最新文章
- java 重写构造函数,覆盖Java中的默认构造函数
- 机器学习特征工程之连续变量离散化:聚类法进行分箱
- deepspeaker(TensorFlow)百度声纹识别和对比代码和模型
- interrupt分析
- 云计算背后的秘密(6)-NoSQL数据库的综述
- ASP.NET MVC 的一个配置节点
- JQuery UI - resizable
- 【iCore3 双核心板_FPGA】例程五:Signal Tapll实验——逻辑分析仪
- 机器学习算法总结--提升方法
- eclipse简单使用
- Bzoj5251: [2018多省省队联测]劈配
- 读芯术python答案_你爱 “Python”的身体,还是“R”的灵魂?
- C语言基础知识:printf的输出格式
- Go 大败!Google 宣布 Fuchsia 终端开发只支持 C/C++/Dart
- iOS学习01C语言数据类型
- zedgraph显示最小刻度_关于ZedGraph几个难点
- 华为+android+root权限获取root,华为emui5.0系统如何root?华为荣耀v8 emui5.0获取7.0系统的root权限方法...
- 【史上最全】常用USB转串口芯片特性比较
- 工作笔记八——vue项目的多语言/国际化插件vue-i18n详解
- Python编程:Python2和Python3环境下re正则匹配中文
热门文章
- word2016 明明设置了默认粘贴为“仅保留文本”,可是每次粘贴的时候还是带源格式怎么办?
- 2022按键精灵内存逆向新教程:乐玩篇:1-10课更新试看
- (科软)高级软件工程课程总结
- 50例源码Python scipy.stats.norm 模块,pdf()
- 【Java】 Java 实现 2048 小游戏
- RCN-Rich feature hierarchies for accurate object detection and semantic segmentation
- python 累加器_python中的累加器
- py语言和php,php和python什么区别
- 计算机视觉 专业术语,计算机视觉中常用的术语.doc
- 51Nod 2069 牛奶 c/c++题解