目录

JavaEE进阶Spring事务

【JavaEE进阶】Spring事务


🍃前言

本篇文章主要内容为以下两个方面

  1. 对事务进行一个简单的介绍
  2. Spring中事务如何实现

🌴事务简介

事务的概念,我相信很多人在数据库的时候已经听过和了解了,所以这里只进行简单的介绍

🚩 什么是事务?

事务是⼀组操作的集合,是⼀个不可分割的操作。

事务会把所有的操作作为⼀个整体,⼀起向数据库提交或者是撤销操作请求。

所以这组操作要么同时成功,要么同时失败。

🚩为什么需要事务?

我们在进行程序开发时,也会有事务的需求.

举个例子吧

比如转账操作,A向B转账

  • 第⼀步:A账⼾-100元.
  • 第⼆步:B账⼾+100元.

如果没有事务,第⼀步执⾏成功了,第⼆步执⾏失败了,那么A账⼾的100元就平⽩⽆故消失了.

如果使⽤事务就可以解决这个问题,让这⼀组操作要么⼀起成功,要么⼀起失败.

🚩事务的操作

事务的操作主要有三步:

  1. 开启事start transaction/begin(⼀组操作前开启事务)
  2. 提交事务:commit(这组操作全部成功,提交事务)
  3. 回滚事务:rollback(这组操作中间任何⼀个操作出现异常,回滚事务)

https://i-blog.csdnimg.cn/direct/0f2f5f08ae55442c9b6a7e6956ad583d.png

🍀Spring 中事务的实现

前⾯课程我们讲了MySQL的事务操作,Spring对事务也进⾏了实现.

Spring 中的事务操作分为两类:

  1. 编程式事务(⼿动写代码操作事务).
  2. 声明式事务(利⽤注解⾃动开启和提交事务).

在学习事务之前,我们先准备数据和数据的访问代码

需求: ⽤⼾注册,注册时在⽇志表中插⼊⼀条操作记录

数据准备: https://i-blog.csdnimg.cn/direct/9661d6e3536b4ccda578f11676a9f9ee.png

代码准备:

    1. 创建项⽬spring-trans,引⼊SpringWeb,Mybatis,mysql等依赖 https://i-blog.csdnimg.cn/direct/b7ef85878a64492092d758aa01b758b7.png
    1. applicant.yml 文件配置: https://i-blog.csdnimg.cn/direct/0c6a2dab74624f568c1e6f59d6ca78a0.png
    1. model(实体类) https://i-blog.csdnimg.cn/direct/59b01be8c8d3467ebc3c15b12b1b663e.png
    1. controller https://i-blog.csdnimg.cn/direct/3c7a0a9e610c4184accfe36a18ae322a.png
    1. service https://i-blog.csdnimg.cn/direct/22665ed131284186aa8f89eda77caeb0.png
    1. model https://i-blog.csdnimg.cn/direct/9f48813d73ec4929866744f52e572c48.png
    1. mapper https://i-blog.csdnimg.cn/direct/f381dec6c0394396996edd30a8de884d.png
  • 测试 https://i-blog.csdnimg.cn/direct/c0e5d87db4034551a2f984a71c27160d.png

🚩Spring 编程式事务

Spring ⼿动操作事务和上⾯MySQL操作事务类似,有3个重要操作步骤:

  • 开启事务(获取事务)
  • 提交事务
  • 回滚事务

SpringBoot 内置了两个对象:

  1. DataSourceTransactionManager 事务管理器.用来获取事务(开启事务),提交或回滚事务的
  2. TransactionDefinition 是事务的属性,在获取事务的时候需要将TransactionDefinition 传递进去从而获得⼀个事务TransactionStatus

我们先来实现一个增加用户,提交事务的案例,操作代码如下: https://i-blog.csdnimg.cn/direct/065b7c38ade14445a3781eeb70d5b28c.png

首先查看当前表中数据: https://i-blog.csdnimg.cn/direct/13181ec2b2664d1699162c6ce94cb7a4.png

通过Postman进行访问: https://i-blog.csdnimg.cn/direct/68fd99838a2b4e00a350423cf67769b1.png

再次查看表中数据: https://i-blog.csdnimg.cn/direct/2f1624e8f1794f49912671206b2c935e.png

没有任何变化,成功进行了回滚操作。

我们再来查看事务的提交:

代码: https://i-blog.csdnimg.cn/direct/90f5407f614a46ca87a2cb385b8d1283.png

首先查看当前表中数据: https://i-blog.csdnimg.cn/direct/1c19b0929a7e4b4abfcc730924c2632b.png

通过Postman进行访问: https://i-blog.csdnimg.cn/direct/84550f4d5a9f478fafd9e34940cd2b64.png

再次查看表中数据: https://i-blog.csdnimg.cn/direct/708083ad555547a29f9ed3b2887afc9a.png

可以看到当前表中并没有id为4的字段,那么可以看出成功的进行了回滚

以上代码虽然可以实现事务,但操作也很繁琐,有没有更简单的实现⽅法呢?

是有的,接下来我们来看一下声命式事务

🚩Spring声明式事务@Transactional

声明式事务的实现很简单

两步操作:

  1. 添加依赖 https://i-blog.csdnimg.cn/direct/4df7c03ebe124d2d888515e26a869668.png

https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=data%3Aimage%2Fgif%3Bbase64%2CR0lGODlhAQABAPABAP%2F%2F%2FwAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw%3D%3D&pos_id=tOhTymLo

​编辑

  1. 在需要事务的⽅法上添加 @Transactional 注解就可以实现了.⽆需⼿动开启事务和提交事 务,进⼊⽅法时⾃动开启事务,⽅法执⾏完会⾃动提交事务,如果中途发⽣了没有处理的异常会⾃动 回滚事务.

代码: https://i-blog.csdnimg.cn/direct/cb5e87adb61d4691a4eea6dcec13c355.png

进行访问: https://i-blog.csdnimg.cn/direct/90f854e7a151404380174053d2a1642c.png

查看数据库,添加成功: https://i-blog.csdnimg.cn/direct/d420ab8116934ae8a2efee275031e9ac.png

使用此注解后,我们还可以从日志中观察到committing表示事务提交成功 https://i-blog.csdnimg.cn/direct/0a1e32e651594da0af37767b71ffa4e3.png

接下来我们修改代码,使该程序出现异常,然后再看看效果 https://i-blog.csdnimg.cn/direct/cf0ec251ad5643a18618d6b89258495c.png

此时,我们再进行访问时,会出现异常 https://i-blog.csdnimg.cn/direct/7523161737d74fcb91fbfe488af6107d.png

数据库数据也没有进行添加 https://i-blog.csdnimg.cn/direct/b814c2af7bff4608b724302ea768d441.png

观察日志,也没有出现committing,说明事务进行了回滚 https://i-blog.csdnimg.cn/direct/e730880bc83f496a9dd55b40b6ef5fb3.png

小结:若加了 @Transactional注解,出现异常会自动的把数据进行回滚;若没有加@Transactional注解,数据会执行成功,不会进行回滚(事务没有开启),在表中显示。

🚩@Transactional的作用

@Transactional 可以用来修饰⽅法或类:

  • 修饰⽅法时:只有修饰public⽅法时才生效(修饰其他⽅法时不会报错,也不生效)[推荐]
  • 修饰类时:对 @Transactional 修饰的类中所有的public⽅法都⽣效

方法/类被 @Transactional 注解修饰时,在目标方法执行开始之前,会自动开启事务,方法执行结束之后,自动提交事务.

  • 如果在方法执行过程中,出现异常,且异常未被捕获,就进行事务回滚操作.
  • 如果异常被程序捕获,⽅法就被认为是成功执行,依然会提交事务.

比如我们对上述代码进行修改,对异常进行捕获 https://i-blog.csdnimg.cn/direct/691fab148b1d4cda967b2567499d1271.png

发送请求后,再次访问查看数据库,发现数据添加成功 https://i-blog.csdnimg.cn/direct/db85ce093bd94a6696e26e97c2ef15af.png

观察日志:这些报红的信息是e.printStackTrace打印的 https://i-blog.csdnimg.cn/direct/9d42a1ee32bd440bba9d5884589d223c.png

运⾏程序,发现虽然程序出错了,但是由于异常被捕获了,所以事务依然得到了提交.

如果想进行回滚,那么有两种方式可以实现

    1. 重新抛出异常 https://i-blog.csdnimg.cn/direct/8e26c8cb60874a4aa92ddebc9e7df0e7.png https://i-blog.csdnimg.cn/direct/0fe17bc9aa284ebe927dd49bfdf69fa9.png
    1. 设置手动回滚 https://i-blog.csdnimg.cn/direct/a179ae7f7cd5434abdafbd93a2dd5d06.png https://i-blog.csdnimg.cn/direct/38abd6abbcef495fbd86f45159791245.png