Spring-4-掌握Spring事务传播机制

今日目标

能够掌握Spring事务配置

Spring事务管理

1 Spring事务简介【重点】

1.1 Spring事务作用

  • 事务作用:在数据层保障一系列的数据库操作同成功同失败

  • Spring事务作用:在数据层或业务层保障一系列的数据库操作同成功同失败

1.2 案例分析Spring事务

  • 需求:实现任意两个账户间转账操作

  • 需求微缩:A账户减钱,B账户加钱

  • 分析: ①:数据层提供基础操作,指定账户减钱(outMoney),指定账户加钱(inMoney) ②:业务层提供转账操作(transfer),调用减钱与加钱的操作 ③:提供2个账号和操作金额执行转账操作 ④:基于Spring整合MyBatis环境搭建上述操作

  • 结果分析: ①:程序正常执行时,账户金额A减B加,没有问题 ②:程序出现异常后,转账失败,但是异常之前操作成功,异常之后操作失败,整体业务失败

  • 结构:

1.3 代码实现

【前置工作】环境准备

创建数据库和表

CREATE DATABASE IF NOT EXISTS `spring_db2` DEFAULT CHARACTER SET utf8
USE `spring_db2`;DROP TABLE IF EXISTS `tbl_account`;
CREATE TABLE `tbl_account` (`id` INT(11NOT NULL AUTO_INCREMENT,`name` VARCHAR(20DEFAULT NULL,`money` DOUBLE DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;INSERT  INTO `tbl_account`(`id`,`name`,`money`VALUES
(1,'Jack',1000),
(2,'Rose',1000);DROP TABLE IF EXISTS `tbl_log`;
CREATE TABLE `tbl_log` (`id` INT(11NOT NULL AUTO_INCREMENT,`info` VARCHAR(255DEFAULT NULL,`create_date` DATETIME DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;INSERT  INTO `tbl_log`(`id`,`info`,`create_date`VALUES
(2,'Jack向Rose转账520.0元','2021-11-04 17:19:18');

pom.xml添加依赖

    <dependencies><!--导入spring的坐标spring-context,对应版本是5.2.10.RELEASE--><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.3.15</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.28</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>5.3.15</version></dependency>
<!--mysql--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.27</version></dependency><!-- https://mvnrepository.com/artifact/org.mybatis/mybatis --><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.13</version></dependency><dependency><groupId>org.mybatis</groupId><artifactId>mybatis-spring</artifactId><version>1.3.0</version></dependency><!-- https://mvnrepository.com/artifact/com.alibaba/druid --><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.2.18</version></dependency><!--junit,spring对junit4的要求必须是4.12版本以上--><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.13</version><scope>test</scope></dependency><!--spring-test--><dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>5.3.22</version><scope>test</scope></dependency></dependencies>

Spring整合Mybatis相关代码(依赖、JdbcConfig、MybatisConfig、SpringConfig) JdbcConfig

package com.zbbmeta.config;import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;import javax.sql.DataSource;public class JdbcConfig {@Value("${jdbc.driverClassName}")private String driverClassName;@Value("${jdbc.url}")private String url;@Value("${jdbc.username}")private String userName;@Value("${jdbc.password}")private String password;@Beanpublic DataSource dataSource(){DruidDataSource ds = new DruidDataSource();ds.setDriverClassName(driverClassName);ds.setUrl(url);ds.setUsername(userName);ds.setPassword(password);return ds;}//spring提供的事务切面类:里面增强了事务管理功能,里面有事务提交和事务回滚功能@Beanpublic PlatformTransactionManager transactionManager(DataSource dataSource){DataSourceTransactionManager ptm = new DataSourceTransactionManager();ptm.setDataSource(dataSource);return ptm;}
}

MybatisConfig

package com.zbbmeta.config;import org.apache.ibatis.logging.stdout.StdOutImpl;
import org.apache.ibatis.session.Configuration;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.context.annotation.Bean;import javax.sql.DataSource;public class MybatisConfig {@Bean  //不仅可以将返回值加入IOC容器,而且可以实现方法参数进行依赖注入,参数默认会根据类型从IOC容器中获取对象自动注入public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean();ssfb.setTypeAliasesPackage("com.zbbmeta.entity"); //告诉mybatis,设置实体类包别名ssfb.setDataSource(dataSource); //将IOC容器中连接池给到Mybatis//注意:必须导入org.apache.ibatis.session.Configuration;Configuration configuration = new Configuration();//设置整合mybatis驼峰命名映射configuration.setMapUnderscoreToCamelCase(true);//设置打印日志configuration.setLogImpl(StdOutImpl.class);ssfb.setConfiguration(configuration);return ssfb;}
}

SpringConfig

@Configuration
@ComponentScan("com.zbbmeta")
@PropertySource("classpath:jdbc.properties")
@Import({JdbcConfig.class,MybatisConfig.class})
public class SpringConfig {
}

Account

@ToString
@Data
public class Account {private Integer id;private String name;private Double money;}

AccountDao

public interface AccountDao {@Update("update tbl_account set money = money + #{money} where name = #{name}")void inMoney(@Param("name") String name, @Param("money") Double money);@Update("update tbl_account set money = money - #{money} where name = #{name}")void outMoney(@Param("name") String name, @Param("money") Double money);
}
public interface AccountService {/*** 转账操作* @param out 传出方* @param in 转入方* @param money 金额*/void transfer(String out,String in,Double money) ;
}

AccountServiceImpl

@Service
public class AccountServiceImpl implements AccountService {@Autowiredprivate AccountDao accountDao;public void transfer(String out, String in, Double money) {//转出accountDao.outMoney(out, money);//模拟出现异常//System.out.println(1 / 0);//转入accountDao.inMoney(in, money);System.out.println("转账成功");}
}

【第一步】在业务层接口上添加Spring事务管理

在转账的方法上添加@Transactional注解

/*** 业务接口*/
public interface AccountService {/*** 转账操作* @param out 传出方* @param in 转入方* @param money 金额*/@Transactionalvoid transfer(String out,String in ,Double money) ;
}

注意事项

  1. Spring注解式事务通常添加在业务层接口中而不会添加到业务层实现类中,降低耦合

  2. 注解式事务可以添加到业务方法上表示当前方法开启事务,也可以添加到接口上表示当前接口所有方法开启事务

【第二步】设置事务管理器(将事务管理器添加到IOC容器中)

说明:可以在JdbcConfig中配置事务管理器

//配置事务管理器,mybatis使用的是jdbc事务
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource){DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();transactionManager.setDataSource(dataSource);return transactionManager;
}

注意事项

  1. 事务管理器要根据实现技术进行选择

  2. MyBatis框架使用的是JDBC事务

【第三步】开启注解式事务驱动

@EnableTransactionManagement

@Configuration
@ComponentScan("com.itheima")
@PropertySource("classpath:jdbc.properties")
@Import({JdbcConfig.class,MybatisConfig.class})
@EnableTransactionManagement //开启事务注解扫描
public class SpringConfig {
}

【第四步】运行测试类,查看结果

  1. 去掉@EnableTransactionManagement注解,即没有进行事务管理,当转账出现问题时结果

  1. 加上@EnableTransactionManagement注解,账户钱都恢复成1000

结果

  1. 转账成功的时候

结果:

@RunWith(SpringJUnit4ClassRunner.class)  //指定第三方的运行器
@ContextConfiguration(classes = SpringConfig.class)  //读取类配置文件
public class AccountDaoTest {@Autowired  //自动注入业务对象private AccountService accountService;@Testpublic void testFindById() throws IOException {//直接使用业务方法accountService.transfer("Jack","Rose"100d);}
}

2 Spring事务角色【理解】

问题导入

什么是事务管理员,什么是事务协调员?

2.1 Spring事务角色

  • 事务管理员:发起事务方,在Spring中通常指代业务层开启事务的方法

  • 事务协调员:加入事务方,在Spring中通常指数据层方法,也可以是业务层方法

3 Spring事务相关配置

问题导入

什么样的异常,Spring事务默认是不进行回滚的?

3.1 @Transactional注解中与事务相关配置

属性作用示例
readOnly设置是否为只读事务readOnly=true 只读事务
timeout设置事务超时时间timeout = -1(永不超时)
rollbackFor设置事务回滚异常(class)rollbackFor = {FileNotFoundException.class}
rollbackForClassName设置事务回滚异常(String)同上格式为字符串
noRollbackFor设置事务不回滚异常(class)noRollbackFor = {FileNotFoundException.class}
noRollbackForClassName设置事务不回滚异常(String)同上格式为字符串
propagation设置事务传播行为……
属性作用示例
readOnly设置是否为只读事务readOnly=true 只读事务
timeout设置事务超时时间timeout = -1(永不超时)
rollbackFor设置事务回滚异常(class)rollbackFor = {FileNotFoundException.class}
rollbackForClassName设置事务回滚异常(String)同上格式为字符串
noRollbackFor设置事务不回滚异常(class)noRollbackFor = {FileNotFoundException.class}
noRollbackForClassName设置事务不回滚异常(String)同上格式为字符串
propagation设置事务传播行为……

说明:对于RuntimeException类型异常或者Error错误,Spring事务能够进行回滚操作。但是对于非运行时异常,Spring事务是不进行回滚的,所以需要使用rollbackFor来设置要回滚的异常。

3.2 案例:转账业务追加日志

需求和分析

  • 需求:实现任意两个账户间转账操作,并对每次转账操作在数据库进行记录

  • 需求微缩:A账户减钱,B账户加钱,数据库记录日志

  • 分析: ①:基于转账操作案例添加日志模块,实现数据库中记录日志 ②:业务层转账操作(transfer),调用减钱、加钱与记录日志功能

  • 实现效果预期: 无论转账操作是否成功,均进行转账操作的日志留痕

  • 存在的问题: 日志的记录与转账操作隶属同一个事务,同成功同失败

  • 实现效果预期改进: 无论转账操作是否成功,日志必须保留

  • 事务传播行为:事务协调员对事务管理员所携带事务的处理态度

【准备工作】环境整备

创建新的LogDao接口

public interface LogDao {@Insert("insert into tbl_log (info,create_date) values(#{info},now())")void log(String info);
}

创建业务接口使用事务

public interface LogService {/*** 记录日志* @param out 转出账户* @param in 转入账户* @param money 金额* 设置事务属性:传播行为设置为需要新事务*/@Transactionalvoid log(String out, String in, Double money);
}

实现类

@Service
public class LogServiceImpl implements LogService {@Autowiredprivate LogDao logDao;public void log(String out, String in, Double money) {logDao.log("转账操作由" + out + "到" + in + ",金额:" + money);}
}

【第一步】在AccountServiceImpl中调用logService中添加日志的方法

因为无论成功与否,都需要记录日志,所以日志放在finally语句块中,但异常不需要捕获,要抛出用来激活事务的处理

/*** 业务接口*/
public interface AccountService {/*** 转账操作* @param out 传出方* @param in 转入方* @param money 金额*/@Transactionalvoid transfer(String out,String in ,Double money) ;
}
@Service
public class AccountServiceImpl implements AccountService {@Autowiredprivate AccountDao accountDao;@Autowiredprivate LogService logService;public void transfer(String out,String in ,Double money) {try{accountDao.outMoney(out,money);//int i = 1/0;accountDao.inMoney(in,money);} finally {logService.log(out,in,money);}}
}

【第二步】在LogService的log()方法上设置事务的传播行为

需求:无论有没有异常日志都要记录下来,不能被回滚

  1. 先只设置@Transactional,查看运行结果

  2. 设置成当前操作需要新事务再查看运行结果

public interface LogService {//propagation设置事务属性:传播行为设置为当前操作需要新事务@Transactional(propagation = Propagation.REQUIRES_NEW)void log(String out, String in, Double money);
}

【第三步】运行测试类,查看结果

@RunWith(SpringJUnit4ClassRunner.class)  //指定第三方的运行器
@ContextConfiguration(classes = SpringConfig.class)  //读取类配置文件
public class AccountServiceTest {@Autowired  //自动注入业务对象private AccountService accountService;/*** 转账的测试*/@Testpublic void testTransfer() {accountService.transfer("Jack""Rose"10d);}
}

思考:为什么 在LogService的方法上设置propagation = Propagation.REQUIRES_NEW接可以创建新的事务?

这就是事务传播行为(经常会面试提问)

3.3 事务传播行为

传播属性Method1Method2
REQUIRED开启TI事务加入T1事务
新建T2事务
REQUIRES_NEW开启TI事务新建T2事务
新建T2事务
SUPPORTS开启TI事务加入T1事务
NOT_SUPPORTED开启TI事务
MANDATORY开启TI事务加入T1事务
ERROR
NEVER开启TI事务ERROR
NESTED

思考:事务会一直生效?有没有事务失效的情况?为什么会失效,大家可以思考一下,这也是一个长问面试题

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/95474.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

算法通关村第八关——轻松搞定翻转二叉树

二叉树有很多经典算法题&#xff0c;今天我们就来看一下二叉树里的翻转问题。 力扣226,给了一棵二叉树&#xff0c;要将二叉树整体翻转。 分析&#xff1a;观察图中翻转前后的二叉树&#xff0c;我们不难发现&#xff0c;翻转过程中&#xff0c;只需要把每一个节点的左右子节点…

Qt使用qml(QtLocation)显示地图

一、qt版本和QtLocation模块版本确认 如果qt版本过低的话是没有QtLocation模块的&#xff0c;我的版本如下 构建工具版本如下 二、qml代码编写 1、工程中添加模块 首先在工程中添加模块quickwidgets positioning location 2、添加资源文件 3、在资源文件中添加qml文件 …

Ribbon 源码分析

Ribbon 源码分析 Ribbon Debug 分析 断点 LoadBalancerInterceptor LoadBalancerInterceptor 实现了 ClientHttpRequestInterceptor 接口&#xff0c;重写了其中的 intercept 方法&#xff0c;用来拦截请求&#xff1b; 获取原始的 uri 和 服务名&#xff0c;调用 LoadBalanc…

matlab中exp和expm的区别

exp()为数组 X 中的每个元素返回指数 e x e^{x} ex expm()计算 X 的矩阵指数。 两个函数传入矩阵后计算的结果是不同的&#xff0c;千万不能混淆。之前曾经想当然得把exp里传入矩阵当矩阵指数使用&#xff0c;也未验证正确性&#xff0c;实不应该。

【2023新教程】树莓派4B开机启动-树莓派第一次启动-树莓派不使用显示器启动-树莓派从购买到启动一步一步完全版!

背景 闲来无事&#xff0c;在咸鱼上买了一个树莓派4B。买来配件都十分齐全&#xff0c;于是就想着启动来测试一下。下面是树莓派无显示器第一次启动的全过程&#xff0c;包含安装系统。 网上的教程大多需要额外使用显示器、鼠标、键盘之类的外设。然而&#xff0c;树莓派本身就…

算法通关村——位运算

1. 常见的位运算 1.1 与 & &&#xff1a;两个数对应的位都是1&#xff0c;那么结果才是1 1 & 1 1 1 & 0 0; 0 & 0 0; 1.2 或 | |: 只要两个数对应的位有一个1&#xff0c;结果就是1 1 | 1 1; 1 | 0 1; 0 | 0 0; 1.3 异或^ ^: 只有两个数的位都…

解决访问Github出现的Couldn‘t connect to server错误

文章目录 前言原因分析以及解决办法原因分析解决办法 参考 前言 在Github上面克隆代码仓库出现Failed to connect to 127.0.0.1 port 1080 after 2063 ms: Couldnt connect to server、Failed to connect to github.com port 443 after 21083 ms: Couldnt connect to server等…

一百六十、Kettle——Linux上安装的Kettle9.2.0连接Hive3.1.2

一、目标 Kettle9.2.0在Linux上安装好后&#xff0c;需要与Hive3.1.2数据库建立连接 之前已经在本地上用kettle9.2.0连上Hive3.1.2 二、各工具版本 &#xff08;一&#xff09;kettle9.2.0 kettle9.2.0安装包网盘链接 链接&#xff1a;https://pan.baidu.com/s/15Zq9w…

Django框架 靓号管理(增删改查)

Django框架 靓号管理&#xff08;增删改查&#xff09; 新建一个项目 backend 使用pycharm创建app startapp app项目目录 C:\code\backend ├── app | ├── admin.py | ├── apps.py | ├── migrations | ├── models.py | ├── tests.py | ├── views.…

js实现按创建时间戳1609459200000 开始往后开始显示运行时长-demo

运行时长 00日 00时 17分 59秒 代码 function calculateRuntime(timestamp) {const startTime Date.now(); // 获取当前时间戳//const runtimeElement document.getElementById(runtime); // 获取显示运行时长的元素function updateRuntime() {const currentTimestamp Date…

1.物联网LWIP网络,TCP/IP协议簇

一。TCP/IP协议簇 1.应用层&#xff1a;FTP&#xff0c;HTTP&#xff0c;Telent&#xff0c;DNS&#xff0c;RIP 2.传输层&#xff1a;TCP&#xff0c;UDP 3.网络层&#xff1a;IPV4&#xff0c;IPV6&#xff0c;OSPF&#xff0c;EIGRP 4.数据链路层&#xff1a;Ethernet&#…

后端返回图片资源错误404,前端使用默认图片

后端返回的图片资源可能会因为各种原因&#xff08;后台误删&#xff0c;地址更改未及时更新&#xff0c;损毁&#xff09;出现无法展示的情况&#xff0c;比如这种报错 就会导致图片资源错误&#xff0c;页面出现这种情况 用户体验很不好&#xff0c;为了改善这种情况&#xf…

36.8k Star! 一款小而美的自动化运维监控工具——Uptime Kuma

自动化运维是指利用计算机科学和信息技术来实现系统和应用程序的自动化管理、监控、维护以及问题解决的过程。它的目标是减少人工干预、提高效率、降低错误率&#xff0c;并确保系统的稳定性和可靠性。 应用简览 Uptime-Kuma 是一个轻量级的监控工具&#xff0c;其独特之处在于…

5G科技防汛,助力守护一方平安

“立秋虽已至&#xff0c;炎夏尚还在”&#xff0c;受台风席卷以及季节性影响全国多地正面临强降水的严峻挑战。“落雨又顺秋&#xff0c;绵绵雨不休”&#xff0c;正值“七下八上” 防汛关键时期&#xff0c;贵州省水文水资源局已全面进入备战状态。 为确保及时响应做好防汛抢…

typedef

t y p e d e f typedef typedef 声明&#xff0c;简称typedef&#xff0c;是创建现有类型的新名字。 比如&#xff1a; #include <bits/stdc.h> using namespace std; typedef long long ll; int main() {ll n;scanf("%lld",&n);printf("%lld"…

echarts tooltip提示框加单位

效果&#xff1a; 1.比较简单的方法 series: [{name: "重大风险",type: "bar",data: data2,color: ExtremeRiskColor,tooltip: {valueFormatter: function (value) {return value 个;}},},{name: "较大风险",type: "bar",data: dat…

rabbitmq的消息应答

消费者完成一个任务可能需要一段时间&#xff0c;如果其中一个消费者处理一个长的任务并仅只完成 了部分突然它挂掉了&#xff0c;会发生什么情况。RabbitMQ 一旦向消费者传递了一条消息&#xff0c;便立即将该消 息标记为删除。在这种情况下&#xff0c;突然有个消费者挂掉了…

如何切换goland之中的版本号(升级go 到1.20)

go 安装/版本切换_go 切换版本_云满笔记的博客-CSDN博客 用brew就行&#xff1a; echo export PATH"/opt/homebrew/opt/go1.20/bin:$PATH" >> ~/.zshrc

HTML详解连载(7)

HTML详解连载&#xff08;7&#xff09; 专栏链接 [link](http://t.csdn.cn/xF0H3)下面进行专栏介绍 开始喽结构伪类选择器作用 :nth-child&#xff08;公式&#xff09;作用举例 伪元素选择器作用注意&#xff1a; PxCoook作用盒子模型-重要组成部分 盒子模型-边框线属性名属性…

Matlab中图例的位置(图例放在图的上方、下方、左方、右方、图外面)等

一、图例默认位置 默认的位置在NorthEast r 10; a 0; b 0; t0:0.1:2.1*pi; xar*cos(t); ybr*sin(t); A1plot(x,y,r,linewidth,4);%圆 hold on axis equal A2plot([0 0],[1 10],b,linewidth,4);%直线 legend([A1,A2],圆形,line)二、通过Location对legend的位置进行改变 变…