SpringBoot 快速开启事务(附常见坑点)
序言:此前,我们主要通过XML配置Spring来托管事务。在SpringBoot则非常简单,只需在业务层添加事务注解(@Transactional )即可快速开启事务。虽然事务很简单,但对于数据方面是需要谨慎对待的,识别常见坑点对我们开发有帮助。
1. 引入依赖
- <!--依赖管理 -->
- <dependencies>
- <dependency> <!--添加Web依赖 -->
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-web</artifactId>
- </dependency>
- <dependency> <!--添加Mybatis依赖 -->
- <groupId>org.mybatis.spring.boot</groupId>
- <artifactId>mybatis-spring-boot-starter</artifactId>
- <version>1.3.1</version>
- </dependency>
- <dependency><!--添加MySQL驱动依赖 -->
- <groupId>mysql</groupId>
- <artifactId>mysql-connector-java</artifactId>
- <scope>runtime</scope>
- </dependency>
- <dependency><!--添加Test依赖 -->
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-test</artifactId>
- <scope>test</scope>
- </dependency>
- </dependencies>
2. 添加配置
主要是配置数据源和开启Mybatis的自动驼峰映射
- @SpringBootApplication
- public class MybatisTransactionApplication {
- public static void main(String[] args) {
- //1.初始化
- SpringApplication application= new SpringApplication(MybatisTransactionApplication.class);
- //2.添加数据源
- Map<String,Object> map = new HashMap<>();
- map.put("spring.datasource.url","jdbc:mysql://localhost:3306/socks?useSSL=false");
- map.put("spring.datasource.username","root");
- map.put("spring.datasource.password","root");
- //3.开启驼峰映射 (Such as account_id ==> accountId)
- map.put("mybatis.configuration.map-underscore-to-camel-case",true);
- application.setDefaultProperties(map);
- //4.启动应用
- application.run(args);
- }
- }
3. 添加数据库记录
打开 Navicat 的查询窗口,然后执行以下SQL:
- DROP TABLE IF EXISTS `account`;
- CREATE TABLE `account` (
- `account_id` varchar(30) ,
- `account_name` varchar(30),
- `balance` decimal(20,2),
- PRIMARY KEY (`account_id`)
- );
- insert into account values ('1','admin','1000.25');
执行完毕后,可以查询到账户数据,如图:

4. 编写代码
以操作账户金额为例,模拟正常操作金额提交事务,以及发生异常回滚事务。
其中控制层代码如下:
- package com.hehe.controller;
- @RestController
- public class AccountController {
- @SuppressWarnings("all")
- @Autowired
- AccountService accountService;
- @GetMapping("/")
- public Account getAccount() {
- //查询账户
- return accountService.getAccount();
- }
- @GetMapping("/add")
- public Object addMoney() {
- try {
- accountService.addMoney();
- } catch (Exception e) {
- return "发生异常了:" + accountService.getAccount();
- }
- return getAccount();
- }
- }
在业务层使用 @Transactional 开启事务,执行数据库操作后抛出异常。具体代码如下:
- package com.hehe.service;
- @Service
- public class AccountService {
- @SuppressWarnings("all")
- @Autowired
- AccountMapper accountMapper;
- public Account getAccount() {
- return accountMapper.getAccount();
- }
- @Transactional
- public void addMoney() throws Exception {
- //先增加余额
- accountMapper.addMoney();
- //然后遇到故障
- throw new RuntimeException("发生异常了..");
- }
- }
数据库层就很简单了,我们通过注解来实现账户数据的查询,具体如下:
- package com.hehe.mapper;
- @Mapper
- public interface AccountMapper {
- @Select("select * from account where account_id=1")
- Account getAccount();
- @Update("update account set balance = balance+100 where account_id=1")
- void addMoney();
- }
其中 Account 实体对象如下:
- package com.hehe.pojo;
- public class Account {
- private String accountId;
- private String accountName;
- private BigDecimal balance;
- // Override toString Method ..
- // Getter & Setters ..
- }
5. 测试事务
启动应用,访问 http://localhost:8080 ,可以看到账户数据,如下:

然后访问 http://localhost:8080/add ,可以看到账户余额并没有增加,如下: 也就是说事务开启成功,数据得到回滚。

6. 常见坑点
使用事务注解@Transactional 之前,应该先了解它的相关属性,避免在实际项目中踩中各种各样的坑点。
常见坑点1:遇到检测异常时,事务默认不回滚。
例如下面这段代码,账户余额依旧增加成功,并没有因为后面遇到SQLException(检测异常)而进行事务回滚!!
- @Transactional
- public void addMoney() throws Exception {
- //先增加余额
- accountMapper.addMoney();
- //然后遇到故障
- throw new SQLException("发生异常了..");
- }
原因分析:因为Spring的默认的事务规则是遇到运行异常(RuntimeException及其子类)和程序错误(Error)才会进行事务回滚,显然SQLException并不属于这个范围。如果想针对检测异常进行事务回滚,可以在@Transactional 注解里使用
rollbackFor 属性明确指定异常。例如下面这样,就可以正常回滚:
- @Transactional(rollbackFor = Exception.class)
- public void addMoney() throws Exception {
- //先增加余额
- accountMapper.addMoney();
- //然后遇到故障
- throw new SQLException("发生异常了..");
- }
常见坑点2: 在业务层捕捉异常后,发现事务不生效。
这是许多新手都会犯的一个错误,在业务层手工捕捉并处理了异常,你都把异常“吃”掉了,Spring自然不知道这里有错,更不会主动去回滚数据。例如:下面这段代码直接导致增加余额的事务回滚没有生效。
- @Transactional
- public void addMoney() throws Exception {
- //先增加余额
- accountMapper.addMoney();
- //谨慎:尽量不要在业务层捕捉异常并处理
- try {
- throw new SQLException("发生异常了..");
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
不要小瞧了这些细节,往前暴露异常很大程度上很能够帮我们快速定位问题,而不是经常在项目上线后出现问题,却无法刨根知道哪里报错。
推荐做法:若非实际业务要求,则在业务层统一抛出异常,然后在控制层统一处理。
- @Transactional
- public void addMoney() throws Exception {
- //先增加余额
- accountMapper.addMoney();
- //推荐:在业务层将异常抛出
- throw new RuntimeException("发生异常了..");
- }
SpringBoot 快速开启事务(附常见坑点)的更多相关文章
- 详解SpringBoot 添加对JSP的支持(附常见坑点)
序言: SpringBoot默认不支持JSP,如果想在项目中使用,需要进行相关初始化工作.为了方便大家更好的开发,本案例可直接作为JSP开发的脚手架工程 SpringBoot+War+JSP . 常见 ...
- Springboot 事务处理常见坑点
使用事务注解@Transactional 之前,应该先了解它的相关属性,避免在实际项目中踩中各种各样的坑点. 常见坑点1:遇到非检测异常时,事务不开启,也无法回滚. 例如下面这段代码,账户余额依旧增加 ...
- springboot开启事务管理
spring中开启事务管理需要在xml配置文件中配置,springboot中采取java config的配置方式. 核心是@EnableTransactionManager注解,该注解即为开启事务管理 ...
- 详解SpringBoot集成jsp(附源码)+遇到的坑
本文介绍了SpringBoot集成jsp(附源码)+遇到的坑 ,分享给大家 1.大体步骤 (1)创建Maven web project: (2)在pom.xml文件添加依赖: (3)配置applica ...
- springboot 开启事务以及手动提交事务
添加依赖,sprongboot 会默认开启事务管理 org.springframework.boot spring-boot-starter-jdbc 在需要的服务类里添加注解 @Autowired ...
- 记一次 Spring 事务配置踩坑记
记一次 Spring 事务配置踩坑记 问题描述:(SpringBoot + MyBatisPlus) 业务逻辑伪代码如下.理论上,插入数据 t1 后,xxService.getXxx() 方法的查询条 ...
- SPRING-BOOT系列之SpringBoot快速入门
今天 , 正式来介绍SpringBoot快速入门 : 可以去如类似 https://docs.spring.io/spring-boot/docs/2.1.0.BUILD-SNAPSHOT/refer ...
- springboot快速使用
1.编写SpringConfig 用于实例化Spring容器 @Configuration //通过该注解来表明该类是一个Spring的配置,相当于一个xml文件 @Bean // 通过该注解来表明是 ...
- springboot 快速开发的定制补充
增强 SpringBoot 快速开发工具 项目地址:https://gitee.com/sanri/web-ui 优点:这是一个 web 通用配置的组件,即插即用,可用于新项目或私活.是对 Sprin ...
- Java开发学习(三十五)----SpringBoot快速入门及起步依赖解析
一.SpringBoot简介 SpringBoot 是由 Pivotal 团队提供的全新框架,其设计目的是用来简化 Spring 应用的初始搭建以及开发过程. 使用了 Spring 框架后已经简化了我 ...
随机推荐
- jenkin创建任务
第一步新建项目 第二步创建任务名称
- java中post推送json格式字符串
最近项目中遇到post推送json格式字符串,之前写过推送json数据,调用失败,才发现是直接推送字符串,只不过字符串是json的格式. 在postman中调用如下: Java中代码如下: /** * ...
- OpenCV Mat类数据存储方式
参考BiliBili 于仕琪老师 avoid-memory-copy-in-opencv class CV_EXPORTS Mat { public: // some members int rows ...
- 论文笔记:Symbolic Execution for Software Testing: Three Decades Later
论文笔记:Symbolic Execution for Software Testing: Three Decades Later 作者 Cristian Cadar 是英国帝国理工学院SRG(Sof ...
- prometheus Alertmanager webhook
一.自定义邮件告警 二.使用docker部署微信机器人告警 1.制作镜像 2.启动容器和指定webhook容器 一.自定义邮件告警 在alertmanager服务的配置文件中指定自定义告警文件 # ...
- redis geo 做距离计算排序分页
redis geo 做距离计算排序分页 // 添加经纬度和店铺id geoadd store_list lng lat store_id 计算距离排序和生成临时文件 georadius store_l ...
- tapdata问题
聚合节点写两个不同的聚合函数,只需要在关联目标节点的目标字段中添加上分组字段,其他字段不用做关联 聚合节点写两个相同的聚合函数,只需要在关联目标节点的目标字段中左右两边都添加上_id,会输出两条数据, ...
- BottomNavigationBar 自定义 底部导航条、以及实现页面切换
一.Flutter BottomNavigationBar 组件 BottomNavigationBar 是底部导航条,可以让我们定义底部 Tab 切换,bottomNavigationBar是 Sc ...
- wrf-python离线安装
由于客户环境不能联网,python的插件库只能离线安装,wrf库的安装中踩了不少坑,特此记录. 1.官方插件库pypi.org只有压缩包,没有提供wheel,在线安装没有问题. 2.下载压缩包解压后, ...
- 维纳攻击 wiener attack
维纳攻击 wiener attack 目录 维纳攻击 wiener attack 攻击条件 使用原理 十三届全国大学生网络安全竞赛 bd 分析 解答 [羊城杯 2020]rrrrrsa (wiener ...