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 框架后已经简化了我 ...
随机推荐
- 关于安装hadoop时在centos上修改主机名总是不成功
按照老师给的文件和网上搜的代码改了很多次,比如改/etc/sysconfig/network这些,无论改几次都没用,找了个帖子,说可能是因为 CentOS7版本由于与之前版本变化比较大,修改主机名的方 ...
- 极米投影仪安装apk的方法
https://www.touying.com/t-37871-1.html 方法二:使用U盘安装:1.使用电脑下载软件apk,并将软件apk的后缀修改为"apk1": 2.然后将 ...
- SCI论文写作技巧-introduction和related works
introduction怎么写 a)背景介绍,现状(介绍别人研究),存在问题,怎样解决,我的做法,有何亮点 b)研究背景和重要性.引出该领域科研空白.点题-指出本文的研究课题.概述文章的核心方法论和主 ...
- php 图片加水印插件
问题:背景透明的水印图片,在加到原图上后不显示,待解决 <?php /** * 图片加水印(适用于png/jpg/gif格式) * * @author flynetcn * * @param $ ...
- 多线程学习(第一天)java语言的线程
一.并发与并行 并发:处理器不停的切换有操作的线程. 并行:多个处理器同时执行有操作线程. 二.启动线程 无论是A,B哪种方式实现多线程,都需要通过Thread.start方法启动线程. A.Thre ...
- Django中关于Manager的使用
首先介绍一下 manager 的使用场景,比如我们有一些表级别的,需要重复使用的功能,都可以使用 manager 来实现. 比如我们在前面的笔记中介绍的 model 的 create().update ...
- 多个el-table在使用v-if在同一页面切换渲染时相互影响的解决办法
解决办法 给每个el-table设置一个唯一的key值,如: <el-table key='uniqueName' ></el-table> <el-table key= ...
- 【音视频】Qt中引入FFmpeg
音视频有关的部分都是使用标准C++的,只有界面的部分才会使用Qt的内容.这样也是为了方便以后移植到其他系统上. 音视频开发中最常做的就是编解码的操作.以H.264为例:如果想要自己实现编码h.264, ...
- vue子组件为父组件属性写值
父组件调用子组件代码(关键字sync): <importModel :visible.sync="dialogModelVisible"></importMode ...
- 2022-3-15内部群每日三题-清辉PMP
1.一家公司被一家大公司收购,这家大公司希望把重点放在其核心产品和服务上.处于规划阶段的所有项目都正在进行修定,以包含新的变更管理程序.一个项目的项目经理需要持续的高级输入,以确保与新的组织结构保持一 ...