序言:此前,我们主要通过XML配置Spring来托管事务。在SpringBoot则非常简单,只需在业务层添加事务注解(@Transactional )即可快速开启事务。虽然事务很简单,但对于数据方面是需要谨慎对待的,识别常见坑点对我们开发有帮助。

1. 引入依赖

  1. <!--依赖管理 -->
  2. <dependencies>
  3. <dependency> <!--添加Web依赖 -->
  4. <groupId>org.springframework.boot</groupId>
  5. <artifactId>spring-boot-starter-web</artifactId>
  6. </dependency>
  7. <dependency> <!--添加Mybatis依赖 -->
  8. <groupId>org.mybatis.spring.boot</groupId>
  9. <artifactId>mybatis-spring-boot-starter</artifactId>
  10. <version>1.3.1</version>
  11. </dependency>
  12. <dependency><!--添加MySQL驱动依赖 -->
  13. <groupId>mysql</groupId>
  14. <artifactId>mysql-connector-java</artifactId>
  15. <scope>runtime</scope>
  16. </dependency>
  17. <dependency><!--添加Test依赖 -->
  18. <groupId>org.springframework.boot</groupId>
  19. <artifactId>spring-boot-starter-test</artifactId>
  20. <scope>test</scope>
  21. </dependency>
  22. </dependencies>

2. 添加配置

主要是配置数据源和开启Mybatis的自动驼峰映射

  1. @SpringBootApplication
  2. public class MybatisTransactionApplication {
  3.  
  4. public static void main(String[] args) {
  5. //1.初始化
  6. SpringApplication application= new SpringApplication(MybatisTransactionApplication.class);
  7.  
  8. //2.添加数据源
  9. Map<String,Object> map = new HashMap<>();
  10. map.put("spring.datasource.url","jdbc:mysql://localhost:3306/socks?useSSL=false");
  11. map.put("spring.datasource.username","root");
  12. map.put("spring.datasource.password","root");
  13.  
  14. //3.开启驼峰映射 (Such as account_id ==> accountId)
  15. map.put("mybatis.configuration.map-underscore-to-camel-case",true);
  16. application.setDefaultProperties(map);
  17.  
  18. //4.启动应用
  19. application.run(args);
  20. }
  21. }

3. 添加数据库记录

打开 Navicat 的查询窗口,然后执行以下SQL:

  1. DROP TABLE IF EXISTS `account`;
  2. CREATE TABLE `account` (
  3. `account_id` varchar(30) ,
  4. `account_name` varchar(30),
  5. `balance` decimal(20,2),
  6. PRIMARY KEY (`account_id`)
  7. );
  8.  
  9. insert into account values ('1','admin','1000.25');

执行完毕后,可以查询到账户数据,如图:

 
 

4. 编写代码

以操作账户金额为例,模拟正常操作金额提交事务,以及发生异常回滚事务。

其中控制层代码如下:

  1. package com.hehe.controller;
  2.  
  3. @RestController
  4. public class AccountController {
  5.  
  6. @SuppressWarnings("all")
  7. @Autowired
  8. AccountService accountService;
  9.  
  10. @GetMapping("/")
  11. public Account getAccount() {
  12. //查询账户
  13. return accountService.getAccount();
  14. }
  15.  
  16. @GetMapping("/add")
  17. public Object addMoney() {
  18. try {
  19. accountService.addMoney();
  20. } catch (Exception e) {
  21. return "发生异常了:" + accountService.getAccount();
  22. }
  23. return getAccount();
  24. }
  25. }

在业务层使用 @Transactional 开启事务,执行数据库操作后抛出异常。具体代码如下:

  1. package com.hehe.service;
  2.  
  3. @Service
  4. public class AccountService {
  5.  
  6. @SuppressWarnings("all")
  7. @Autowired
  8. AccountMapper accountMapper;
  9.  
  10. public Account getAccount() {
  11. return accountMapper.getAccount();
  12. }
  13.  
  14. @Transactional
  15. public void addMoney() throws Exception {
  16. //先增加余额
  17. accountMapper.addMoney();
  18. //然后遇到故障
  19. throw new RuntimeException("发生异常了..");
  20. }
  21. }

数据库层就很简单了,我们通过注解来实现账户数据的查询,具体如下:

  1. package com.hehe.mapper;
  2.  
  3. @Mapper
  4. public interface AccountMapper {
  5.  
  6. @Select("select * from account where account_id=1")
  7. Account getAccount();
  8.  
  9. @Update("update account set balance = balance+100 where account_id=1")
  10. void addMoney();
  11. }

其中 Account 实体对象如下:

  1. package com.hehe.pojo;
  2.  
  3. public class Account {
  4.  
  5. private String accountId;
  6. private String accountName;
  7. private BigDecimal balance;
  8.  
  9. // Override toString Method ..
  10. // Getter & Setters ..
  11. }

5. 测试事务

启动应用,访问 http://localhost:8080 ,可以看到账户数据,如下:

 
 

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

 
 

6. 常见坑点

使用事务注解@Transactional 之前,应该先了解它的相关属性,避免在实际项目中踩中各种各样的坑点。

常见坑点1:遇到检测异常时,事务默认不回滚。

例如下面这段代码,账户余额依旧增加成功,并没有因为后面遇到SQLException(检测异常)而进行事务回滚!!

  1. @Transactional
  2. public void addMoney() throws Exception {
  3. //先增加余额
  4. accountMapper.addMoney();
  5. //然后遇到故障
  6. throw new SQLException("发生异常了..");
  7. }

原因分析:因为Spring的默认的事务规则是遇到运行异常(RuntimeException及其子类)和程序错误(Error)才会进行事务回滚,显然SQLException并不属于这个范围。如果想针对检测异常进行事务回滚,可以在@Transactional 注解里使用

rollbackFor 属性明确指定异常。例如下面这样,就可以正常回滚:

  1. @Transactional(rollbackFor = Exception.class)
  2. public void addMoney() throws Exception {
  3. //先增加余额
  4. accountMapper.addMoney();
  5. //然后遇到故障
  6. throw new SQLException("发生异常了..");
  7. }

常见坑点2: 在业务层捕捉异常后,发现事务不生效。

这是许多新手都会犯的一个错误,在业务层手工捕捉并处理了异常,你都把异常“吃”掉了,Spring自然不知道这里有错,更不会主动去回滚数据。例如:下面这段代码直接导致增加余额的事务回滚没有生效。

  1. @Transactional
  2. public void addMoney() throws Exception {
  3. //先增加余额
  4. accountMapper.addMoney();
  5. //谨慎:尽量不要在业务层捕捉异常并处理
  6. try {
  7. throw new SQLException("发生异常了..");
  8. } catch (Exception e) {
  9. e.printStackTrace();
  10. }
  11. }

不要小瞧了这些细节,往前暴露异常很大程度上很能够帮我们快速定位问题,而不是经常在项目上线后出现问题,却无法刨根知道哪里报错。

推荐做法:若非实际业务要求,则在业务层统一抛出异常,然后在控制层统一处理。

  1. @Transactional
  2. public void addMoney() throws Exception {
  3. //先增加余额
  4. accountMapper.addMoney();
  5. //推荐:在业务层将异常抛出
  6. throw new RuntimeException("发生异常了..");
  7. }

SpringBoot 快速开启事务(附常见坑点)的更多相关文章

  1. 详解SpringBoot 添加对JSP的支持(附常见坑点)

    序言: SpringBoot默认不支持JSP,如果想在项目中使用,需要进行相关初始化工作.为了方便大家更好的开发,本案例可直接作为JSP开发的脚手架工程 SpringBoot+War+JSP . 常见 ...

  2. Springboot 事务处理常见坑点

    使用事务注解@Transactional 之前,应该先了解它的相关属性,避免在实际项目中踩中各种各样的坑点. 常见坑点1:遇到非检测异常时,事务不开启,也无法回滚. 例如下面这段代码,账户余额依旧增加 ...

  3. springboot开启事务管理

    spring中开启事务管理需要在xml配置文件中配置,springboot中采取java config的配置方式. 核心是@EnableTransactionManager注解,该注解即为开启事务管理 ...

  4. 详解SpringBoot集成jsp(附源码)+遇到的坑

    本文介绍了SpringBoot集成jsp(附源码)+遇到的坑 ,分享给大家 1.大体步骤 (1)创建Maven web project: (2)在pom.xml文件添加依赖: (3)配置applica ...

  5. springboot 开启事务以及手动提交事务

    添加依赖,sprongboot 会默认开启事务管理 org.springframework.boot spring-boot-starter-jdbc 在需要的服务类里添加注解 @Autowired ...

  6. 记一次 Spring 事务配置踩坑记

    记一次 Spring 事务配置踩坑记 问题描述:(SpringBoot + MyBatisPlus) 业务逻辑伪代码如下.理论上,插入数据 t1 后,xxService.getXxx() 方法的查询条 ...

  7. SPRING-BOOT系列之SpringBoot快速入门

    今天 , 正式来介绍SpringBoot快速入门 : 可以去如类似 https://docs.spring.io/spring-boot/docs/2.1.0.BUILD-SNAPSHOT/refer ...

  8. springboot快速使用

    1.编写SpringConfig 用于实例化Spring容器 @Configuration //通过该注解来表明该类是一个Spring的配置,相当于一个xml文件 @Bean // 通过该注解来表明是 ...

  9. springboot 快速开发的定制补充

    增强 SpringBoot 快速开发工具 项目地址:https://gitee.com/sanri/web-ui 优点:这是一个 web 通用配置的组件,即插即用,可用于新项目或私活.是对 Sprin ...

  10. Java开发学习(三十五)----SpringBoot快速入门及起步依赖解析

    一.SpringBoot简介 SpringBoot 是由 Pivotal 团队提供的全新框架,其设计目的是用来简化 Spring 应用的初始搭建以及开发过程. 使用了 Spring 框架后已经简化了我 ...

随机推荐

  1. jenkin创建任务

    第一步新建项目 第二步创建任务名称

  2. java中post推送json格式字符串

    最近项目中遇到post推送json格式字符串,之前写过推送json数据,调用失败,才发现是直接推送字符串,只不过字符串是json的格式. 在postman中调用如下: Java中代码如下: /** * ...

  3. OpenCV Mat类数据存储方式

    参考BiliBili 于仕琪老师 avoid-memory-copy-in-opencv class CV_EXPORTS Mat { public: // some members int rows ...

  4. 论文笔记:Symbolic Execution for Software Testing: Three Decades Later

    论文笔记:Symbolic Execution for Software Testing: Three Decades Later 作者 Cristian Cadar 是英国帝国理工学院SRG(Sof ...

  5. prometheus Alertmanager webhook

      一.自定义邮件告警 二.使用docker部署微信机器人告警 1.制作镜像 2.启动容器和指定webhook容器 一.自定义邮件告警 在alertmanager服务的配置文件中指定自定义告警文件 # ...

  6. redis geo 做距离计算排序分页

    redis geo 做距离计算排序分页 // 添加经纬度和店铺id geoadd store_list lng lat store_id 计算距离排序和生成临时文件 georadius store_l ...

  7. tapdata问题

    聚合节点写两个不同的聚合函数,只需要在关联目标节点的目标字段中添加上分组字段,其他字段不用做关联 聚合节点写两个相同的聚合函数,只需要在关联目标节点的目标字段中左右两边都添加上_id,会输出两条数据, ...

  8. BottomNavigationBar 自定义 底部导航条、以及实现页面切换

    一.Flutter BottomNavigationBar 组件 BottomNavigationBar 是底部导航条,可以让我们定义底部 Tab 切换,bottomNavigationBar是 Sc ...

  9. wrf-python离线安装

    由于客户环境不能联网,python的插件库只能离线安装,wrf库的安装中踩了不少坑,特此记录. 1.官方插件库pypi.org只有压缩包,没有提供wheel,在线安装没有问题. 2.下载压缩包解压后, ...

  10. 维纳攻击 wiener attack

    维纳攻击 wiener attack 目录 维纳攻击 wiener attack 攻击条件 使用原理 十三届全国大学生网络安全竞赛 bd 分析 解答 [羊城杯 2020]rrrrrsa (wiener ...