(十)springboot实战——springboot3下的webflux项目mysql数据库事务处理

前言

WebFlux 是 Spring Framework 5.0 中引入的一种新型反应式编程模型,支持非阻塞 I/O,适用于高并发、高吞吐量的应用程序。在 WebFlux 应用程序中使用事务需要注意以下几点。使用 Reactive R2DBC:WebFlux 支持使用 Reactive R2DBC 访问关系型数据库。R2DBC 是一个反应式的数据库连接规范,它允许开发人员以响应式方式访问关系型数据库。在 WebFlux 应用程序中使用 Reactive R2DBC 可以实现非阻塞式的数据库操作。使用 @Transactional 注解:在 WebFlux 应用程序中,可以使用 @Transactional 注解声明事务边界,将多个数据库操作绑定到同一事务中。需要注意的是,@Transactional 注解需要与 Reactive R2DBC 结合使用,确保事务管理器与 R2DBC 兼容。遵循响应式编程原则:在 WebFlux 应用程序中,需要遵循响应式编程的原则,使用 Mono 和 Flux 等响应式类型来处理数据流,而不是传统的阻塞式方法。这意味着在事务中涉及到的所有操作,都需要返回 Mono 或 Flux 对象,并且保证响应式链条的正确性。

本节内容以关系型数据库mysql为例,通过使用spring-boot-starter-data-r2dbc框架完成关系型数据库的调用,并通过具体的案例实现webflux应用下的数据库事务管理,包含最佳的使用实战案例,通过spring的AOP实现最终的事务控制。

正文

①引入必要的pom组件,其中spring-boot-starter-aop可以不引入,如果不使用aop管理r2dbc的数据库事务

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId>
</dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId>
</dependency><!-- https://mvnrepository.com/artifact/org.springdoc/springdoc-openapi-starter-webflux-ui -->
<dependency><groupId>org.springdoc</groupId><artifactId>springdoc-openapi-starter-webflux-ui</artifactId><version>2.3.0</version>
</dependency><!--        响应式 Spring Data R2dbc-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-r2dbc</artifactId>
</dependency><!-- https://mvnrepository.com/artifact/io.asyncer/r2dbc-mysql -->
<dependency><groupId>io.asyncer</groupId><artifactId>r2dbc-mysql</artifactId><version>1.0.6</version>
</dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-webflux</artifactId>
</dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope>
</dependency>
<dependency><groupId>io.projectreactor</groupId><artifactId>reactor-test</artifactId><scope>test</scope>
</dependency>

②创建控制层UserController请求接口测试方法:新增用户

@Operation(summary = "新增用户", description = "新增用户")
@PostMapping(value = "saveUser")
public Mono<ApiResponse> saveUser(@RequestBody User user) {Mono<User> userMono = userService.saveUser(user);return  userMono.map(ApiResponse::success);
}

③创建UserService的业务接口层

/*** 新增用户** @param user* @return*/
Mono<User> saveUser(User user);

 ④创建UserServiceImpl的业务实现层

    @Overridepublic Mono<User> saveUser(User user) {return userRepository.save(user);}

 ⑤创建UserRepository数据操作层

package com.yundi.atp.repository;import com.yundi.atp.entity.User;
import org.springframework.data.r2dbc.repository.Query;
import org.springframework.data.r2dbc.repository.R2dbcRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import reactor.core.publisher.Mono;@Repository
public interface UserRepository extends R2dbcRepository<User, Integer> {/*** 根据用户名获取用户信息** @param name* @return*/Mono<User> findUsersByName(@Param("name") String name);/*** 更新用户信息** @param user* @return*/@Query("update user set name = :#{#user.name}, age = :#{#user.age} where id = :#{#user.id}")Mono<User> updateUser(@Param("user") User user);/*** 动态更新用户信息** @param user* @return*/@Query("UPDATE User SET " +"name = CASE WHEN :#{#user.name} IS NULL THEN name ELSE :#{#user.name} END, " +"age = CASE WHEN :#{#user.age} IS NULL THEN age ELSE :#{#user.age} END " +"WHERE id = :#{#user.id}")Mono<User> updateUserQuery(@Param("user") User user);
}

⑥ 正常通过swagger工具访问用户新增接口,数据可以正常插入数据库

⑦在保存用户的业务方法中人为创建一个异常,验证是否能够正常保存数据

@Override
public Mono<User> saveUser(User user) {Mono<User> monoUser = userRepository.save(user);//人为创建一个数学异常System.out.println(1/0);return monoUser;
}

 ⑧使用swagger工具访问用户新增接口,从打印的日志来看,数据并没有插入成功,并抛出了异常,但是这里并没有数据库的事务处理

PS:这里可能会误导初学者,认为webflux的r2dbc具备天然的事务处理机制,其实不然,这是因为webflux是响应式非阻塞式编程,所有操作都是异步执行,导致业务方法会跳过用户保存的操作而先去执行异常的部分代码,直接消费异常处理结果,真正的用户保存方法并没有执行。

⑨使用@Transactional,加入事务处理注解,查看是否会有事务执行

@Transactional(rollbackFor = Exception.class)
@Override
public Mono<User> saveUser(User user) {Mono<User> mono = userRepository.save(user);System.out.println(1/0);return mono;
}

⑩使用swagger工具访问用户新增接口,从打印的日志来看,加入@Transactional确实切入了事务,但是由于异步执行,跳过了用户保存的方法,用户保存sql并未执行,这里的事务看起来并没有起什么作用

⑪使用Mono的flatMap方法将业务方法改为如下方式,先执行用户保存方法,在触发异常,查看打印结果

@Transactional(rollbackFor = Exception.class)
@Override
public Mono<User> saveUser(User user) {return userRepository.save(user).flatMap(item -> {System.out.println("item:" + item);System.out.println(1 / 0);return Mono.just(item);});
}

⑫使用swagger工具访问用户新增接口,从打印的日志来看,数据持久化sql操作真实执行了,但并没有真实插入数据库,事务确定已经真实生效,数据进行了回滚

⑬利用事务的传播机制,测试内层事务出现异常,内外层事务都会回滚

@Transactional(rollbackFor = Exception.class)
@Override
public Mono<User> saveUser(User user) {return userRepository.save(user).flatMap(item -> {System.out.println("item:" + item);return userRepository.findUsersByName(item.getName()).flatMap(it -> {System.out.println("it:" + it);System.out.println(1 / 0);return Mono.just(it);});});
}

⑭使用swagger工具访问用户新增接口,从打印的日志来看,内外层的sql都执行了,由于内层异常,导致全局的异常回滚,说明异常的传播机制是shengx

⑮最佳实践:使用aop切面实现数据库事务的统一管理,创建全局事务处理配置类ReactiveTransactionConfig

package com.yundi.atp.config;import io.r2dbc.spi.ConnectionFactory;
import jakarta.annotation.Resource;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.r2dbc.connection.R2dbcTransactionManager;
import org.springframework.transaction.ReactiveTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.interceptor.DefaultTransactionAttribute;
import org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource;
import org.springframework.transaction.interceptor.TransactionInterceptor;@Configuration
@EnableTransactionManagement
public class ReactiveTransactionConfig {@Resourceprivate ConnectionFactory connectionFactory;@Beanpublic ReactiveTransactionManager reactiveTransactionManager() {return new R2dbcTransactionManager(connectionFactory);}@Bean(name = "myTransactionInterceptor")public TransactionInterceptor myTransactionInterceptor() {//写事务控制DefaultTransactionAttribute txAttr_REQUIRED = new DefaultTransactionAttribute();txAttr_REQUIRED.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);txAttr_REQUIRED.setTimeout(2000);//读事务控制DefaultTransactionAttribute txAttr_REQUIRED_READONLY = new DefaultTransactionAttribute();txAttr_REQUIRED_READONLY.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);NameMatchTransactionAttributeSource source = new NameMatchTransactionAttributeSource();source.addTransactionalMethod("page*", txAttr_REQUIRED_READONLY);source.addTransactionalMethod("find*", txAttr_REQUIRED_READONLY);source.addTransactionalMethod("query*", txAttr_REQUIRED_READONLY);source.addTransactionalMethod("list*", txAttr_REQUIRED_READONLY);source.addTransactionalMethod("get*", txAttr_REQUIRED_READONLY);source.addTransactionalMethod("save*", txAttr_REQUIRED);source.addTransactionalMethod("add*", txAttr_REQUIRED);source.addTransactionalMethod("update*", txAttr_REQUIRED);source.addTransactionalMethod("remove*", txAttr_REQUIRED);source.addTransactionalMethod("delete*", txAttr_REQUIRED);return new TransactionInterceptor(reactiveTransactionManager(), source);}/*** 切点** @return*/@Beanpublic Advisor advisor() {AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();pointcut.setExpression("execution(* com.yundi.atp.service..*(..))");return new DefaultPointcutAdvisor(pointcut, myTransactionInterceptor());}}

⑯使用swagger工具访问用户新增接口,从打印的日志来看,不加@Transactional,依然可以实现事务处理

结语

至此,关于webflux项目数据库事务的内容到这里就结束了,我们下期见。。。。。。

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

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

相关文章

WebService的services.xml问题

WebService有多种实现方式&#xff0c;这里使用的是axis2 问题&#xff1a; 在本地开发&#xff0c;访问本地的http://localhost:8080/services/ims?wsdl&#xff0c;正常访问 但是打成jar包&#xff0c;不管是linux还是window启动&#xff0c;都访问不到&#xff0c;报错…

金线检测步骤

半导体行业,金线检测是必不可以少的一个检测项,除了焊点,die面,手指以外的必检项目. 重难点在于金线的提取,算法多种多样,找到适合才是关键,涉及到打光,图像处理,这里不做深入分析,软件和硬件配合好才能做的最好. 经典算法Block分析,结合图像检测. 高斯算法提取 边缘检测算法提…

空间域:空间组学的耶路撒冷

文章目录 环境配置与数据SquidpySpaGCN将基因表达和组织学整合到一个图上基因表达数据质控与预处理SpaGCN的超参优化空间域 参考文献 空间组学不能没有空间域&#xff0c;就如同蛋白质不能没有结构域。 摘要&#xff1a; 空间域是反映细胞在基因表达方面的相似性以及空间邻近性…

【Android Studio 启动出错】

Android Studio版本&#xff1a;2022.3.1 出错前操作&#xff1a; 昨晚开着三四个项目&#xff0c;然后太晚了直接关机睡觉&#xff0c;第二天起来开机&#xff0c;启动Android Studio&#xff0c;就出现了这个问题&#xff1a; Internal error. Please refer to https://co…

网络防御保护——课程笔记

一.防火墙 防火墙的主要职责&#xff1a;控制和防护 --- 安全策略 --- 防火墙可以根据安全策略来抓取流量之后做出对应的动作。 防火墙的分类 防火墙的发展进程 防火墙的控制 带内管理 --- 通过网络环境对设备进行控制 --- telnet&#xff0c;ssh&#xff0c;web --- 登录设备…

C语言——如何进行文件操作

大家好&#xff0c;我是残念&#xff0c;希望在你看完之后&#xff0c;能对你有所帮助&#xff0c;有什么不足请指正&#xff01;共同学习交流 本文由&#xff1a;残念ing原创CSDN首发&#xff0c;如需要转载请通知 个人主页&#xff1a;残念ing-CSDN博客&#xff0c;欢迎各位→…

提升编程效率的利器: 解析Google Guava库之集合篇RangeSet范围集合(五)

在编程中&#xff0c;我们经常需要处理各种范围集合&#xff0c;例如时间范围、数字范围等。传统的集合类库往往只能处理离散的元素集合&#xff0c;对于范围集合的处理则显得力不从心。为了解决这个问题&#xff0c;Google的Guava库提供了一种强大的数据结构——RangeSet&…

计算机网络-物理层传输介质(导向传输介质-双绞线 同轴电缆 光纤和非导向性传输介质-无线波 微波 红外线 激光)

文章目录 传输介质及分类导向传输介质-双绞线导向传输介质-同轴电缆导向传输介质-光纤非导向性传输介质小结 传输介质及分类 物理层规定电气特性&#xff1a;规定电气信号对应的数据 导向传输介质-双绞线 双绞线的主要作用是传输数据和语音信息。它通过将两根导线以特定的方…

C#,德兰诺依数(Dealnnoy Number)的算法与源代码

1 Dealnnoy Number 德兰诺依数&#xff0c;德兰诺伊数 德兰诺依数是以法国军官、业余数学家亨利德兰诺依&#xff08;Henry Dealnnoy&#xff09;的名字命名。 Henry Dealnnoy 在组合数学中&#xff0c;德兰诺依数描述了从(0,0)到(m,n)的格路问题中&#xff0c; 只允许按照(0…

vue3之echarts3D环柱饼图

vue3之echarts3D环柱饼图 效果&#xff1a; 版本 "echarts": "^5.4.1", "echarts-gl": "^2.0.9" 核心代码&#xff1a; <template><div class"content"><div ref"eCharts" class"chart&…

【uniapp·微信登录】

一、新建文件夹&#xff08;登录&#xff09; 在HBuilderX中开发微信小程序的步骤如下&#xff1a; 在HBuilderX中新建一个uniapp项目。 在HBuilderX中配置安装的微信开发者工具路径&#xff0c;可以通过点击 工具》设置》运行配置–小程序运行配置 进行配置。 在HBuilderX中修…

动网格-网格重构之弹性光顺局部重构法(四)

弹性光顺法的基本特点 弹性光顺法中&#xff0c;网格线类似于弹簧&#xff0c;两端节点(node)作弹性移动 弹性光顺法有如下特点。 (1)节点的数量和节点之间的连接关系均不变&#xff0c;即节点之间的连接属性不变。 (2)单独使用时&#xff0c;仅限于变形非常小的情况&#xff…

while 和 do-while

签名&#xff1a;但行好事&#xff0c;莫问前程。 文章目录 前言一、while1、基本语法2、执行过程3、示例 二、do-while1、基本语法2、执行过程3、小练习&#xff08;ATM存款取款机&#xff09; 总结 前言 记录一下while 和 do-while 的使用。 一、while 1、基本语法 ①初始…

OpenHarmony—ArkTS限制throw语句中表达式的类型

规则&#xff1a;arkts-limited-throw 级别&#xff1a;错误 ArkTS只支持抛出Error类或其派生类的实例。禁止抛出其他类型&#xff08;例如number或string&#xff09;的数据。 TypeScript throw 4; throw ; throw new Error();ArkTS throw new Error();限制省略函数返回类…

漏油检测时间大幅缩短!漏油传感器的检测原理是什么?

在油类化工厂、输油管道、油库等工业生产场所&#xff0c;漏油情况时有发生&#xff0c;如果不能及时发现&#xff0c;往往产生非常严重的后果。因此&#xff0c;由漏油控制器和漏油检测绳组合而成的漏油传感器被广泛应用了起来&#xff0c;能够在发生漏油时及时发出告警&#…

AHK学习,诡异的早起,舒畅地打篮球——2024 第4周总结

活神仙 引言颓 周六周日理清当前老问题新问题 总结当前之前的老问题 学习的AHKAHK历程AHK作用和适合人群 我帮别人解决的AHK例子我自用的AKH功能结尾 引言 今天才写周总结 是因为这两天有点颓 颓在哪里呢&#xff1f; 请听我细细说来 水文 技术有 AHK的&#xff0c;不想看可以…

【React教程】(1) React简介、React核心概念、React初始化

目录 ReactReact 介绍React 特点React 的发展历史React 与 Vue 的对比技术层面开发团队社区Native APP 开发 相关资源链接 EcmaScript 6 补充React 核心概念组件化虚拟 DOM 起步初始化及安装依赖Hello World React React 介绍 React 是一个用于构建用户界面的渐进式 JavaScrip…

【DC-DC】AP5125 降压恒流驱动器 60W LED电源驱动方案PCB+BOM表

这是一款60WLED驱动方案,线路图如下 ​ 祥单表&#xff1a; 实物图&#xff1a; 产品描述 特点应用领域应用原理图AP5125 是一款外围电路简单的 Buck 型平均电流检测模式的 LED 恒流驱动器&#xff0c;适用于 8-100V 电压范围的非隔离式大功率恒流 LED 驱动领域。芯片采用固定…

APUE学习之进程间通信(IPC)(下篇)

目录 一、进程间通信&#xff08;IPC&#xff09; 二、信号量&#xff08;Semaphore&#xff09; 1、基本概念 2、同步关系与互斥关系 3、临界区与临界资源 4、信号量的工作原理 5、信号量编程 6、实战演练 三、共享内存&#xff08;Shared Memory&#xff09; 1、…

如何使用 Maltego 情报调查保姆级教程(附链接)

前言 使用软件需要挂梯子 一、介绍 Maltego 是一种开放源代码的情报和数据连接工具&#xff0c;专注于网络情报收集和图形化分析。它为用户提供了一个交互式的界面&#xff0c;用于收集、分析和可视化有关目标的信息。Maltego 被广泛用于网络侦查、威胁情报、渗透测试和安全…