百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术教程 > 正文

SpringBoot WebFlux整合R2DBC实现数据库反应式编程

mhr18 2025-07-09 15:30 4 浏览 0 评论

环境:Springboot2.4.12


R2DBC简介

Spring data R2DBC是更大的Spring data 系列的一部分,它使得实现基于R2DBC的存储库变得容易。R2DBC代表反应式关系数据库连接,这是一种使用反应式驱动程序集成SQL数据库的规范。Spring Data R2DBC使用属性的Spring抽象和Repository支持应用于R2DBC。它使得在反应式应用程序堆栈中使用关系数据访问技术构建Spring驱动的应用程序变得更加容易。

Spring Data R2DBC的目标是在概念上变得简单。为了实现这一点,它不提供缓存、延迟加载、写后处理或ORM框架的许多其他特性。这使得Spring Data R2DBC成为一个简单、有限、固执己见的对象映射器。

Spring Data R2DBC允许一种 functional 方法与数据库交互,提供R2dbcEntityTemplate作为应用程序的入口点。

首先选择数据库驱动程序并创建R2dbcEntityTemplate实例:

  • H2 (io.r2dbc:r2dbc-h2)
  • MariaDB (org.mariadb:r2dbc-mariadb)
  • Microsoft SQL Server (io.r2dbc:r2dbc-mssql)
  • MySQL (dev.miku:r2dbc-mysql)
  • jasync-sql MySQL (com.github.jasync-sql:jasync-r2dbc-mysql)
  • Postgres (io.r2dbc:r2dbc-postgresql)
  • Oracle (com.oracle.database.r2dbc:oracle-r2dbc)

WebFlux介绍

Spring框架中包含原始web框架Spring Web MVC是专门为ServletAPI和Servlet容器构建的。反应式堆栈web框架Spring Web Flux是后来在5.0版中添加。它是完全非阻塞的,支持反应流背压(由消费者控制生产者的速度),并在Netty、Undertow和Servlet 3.1+容器等服务器上运行。

这两个web框架都反映了它们的源模块(Spring Web MVC和Spring Web Flux)的名称,并在Spring框架中共存。每个模块都是可选的。应用程序可以使用一个或另一个模块,在某些情况下,可以同时使用这两个模块 — 例如,带有反应式WebClient的Spring MVC控制器。

依赖管理

<dependencies>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
  </dependency>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-r2dbc</artifactId>
  </dependency>
  <dependency>
    <groupId>dev.miku</groupId>
    <artifactId>r2dbc-mysql</artifactId>
  </dependency>
</dependencies>

应用配置

spring:
  r2dbc:
    url: r2dbc:mysql://localhost:3306/reactive_db
    username: root
    password: 123123
    pool:
      initialSize: 100
      maxSize: 100
---
logging:
  level:
    org.springframework.r2dbc: DEBUG  #输出执行的sql

关于MySQL的R2DBC详细配置查看:

https://github.com/mirromutth/r2dbc-mysql

实体&Service

@Table("T_USERS")
public class Users {
  @Id
  private Long id ;
  private String name ;
  private String sex ;
  private Integer age ;
}

Service

@Resource
private R2dbcEntityTemplate template ;
	
@Transactional
public Mono<Users> insertByTemplate(Users users) {
  return template.insert(users) ;
}
public Mono<Integer> removeByTemplate(Long id) {
  Query query = Query.query(Criteria.where("id").is(id)) ;
  return template.delete(query, Users.class) ;
}
public Mono<Integer> updateByTemplate(Users users) {
  CriteriaDefinition criteria = Criteria.where("id").is(users.getId()) ;
  Query query = Query.query(criteria) ;
  Update update = Update.update("name", users.getName()) ;
  return template.update(query, update, Users.class) ;
}
public Mono<Users> selectByTemplate(Long id) {
  Query query = Query.query(Criteria.where("id").is(id)) ;
  return template.select(query, Users.class).single() ;
}
public Flux<Users> selectByTemplate(Integer page, Integer size) {
  Query query =  Query.empty().offset((page - 1) * size).limit(size) ;
  return template.select(query, Users.class) ;
}
public Mono<Long> selecyByTemplateCount() {
  return template.select(Users.class).count() ;
}
public Mono<ResponseEntity<List<Users>>> selectByTemplatePager(Integer page, Integer size) {
  Mono<List<Users>> datas = this.selectByTemplate(page, size).collectList() ;
  Mono<Long> count = this.selecyByTemplateCount() ;
  return datas.zipWith(count, (list, c) -> {
    return ResponseEntity.ok().header("count", c + "").header("page", page + "").header("size", size + "").body(list) ;
  }) ;
}

Controller

@Resource
private UsersService us ;
@PostMapping("/insert")
public Mono<Users> insertByTemplate(@RequestBody Users users) {
  return us.insertByTemplate(users) ;
}
@GetMapping("/remove/{id}")
public Mono<Integer> removeByTemplate(@PathVariable("id")Long id) {
  return us.removeByTemplate(id) ;
}
@PostMapping("/update")
public Mono<Integer> updateByTemplate(@RequestBody Users users) {
  return us.updateByTemplate(users) ;
}
@GetMapping("/query/{id}")
public Mono<Users> selectByTemplate(@PathVariable("id") Long id) {
  return us.selectByTemplate(id).single() ;
}
@GetMapping("/pager")
public Mono<ResponseEntity<List<Users>>> selectByTemplate(Integer page, Integer size) {
  return us.selectByTemplatePager(page, size) ;
}
@GetMapping("/count")
public Mono<Long> selecyByTemplateCount() {
  return us.selecyByTemplateCount() ;
}

R2DBC Repository

通过继承ReactiveCrudRepository或者是ReactiveSortingRepository。Repository支持的方法查询如下表所示:

Repository修改操作:

interface ReactivePersonRepository extends ReactiveSortingRepository<Person, String> {
  Mono<Integer> deleteByLastname(String lastname);            
  Mono<Void> deletePersonByLastname(String lastname);         
  Mono<Boolean> deletePersonByLastname(String lastname);      
}

自定义修改操作:

@Modifying
@Query("UPDATE person SET firstname = :firstname where lastname = :lastname")
Mono<Integer> setFixedFirstnameFor(String firstname, String lastname);

支持乐观锁:

@Version注释提供了与R2DBC上下文中JPA类似的语法,并确保更新仅应用于具有匹配版本的行。因此,version属性的实际值被添加到update查询中,如果另一个操作同时更改了行,则更新不会产生任何影响。在这种情况下,将抛出
OptimisticLockingFailureException。以下示例显示了这些功能:

@Table
public class Person {
  @Id Long id;
  String firstname;
  String lastname;
  @Version 
  Long version;
}

如下示例演示了乐观锁异常的触发:

R2dbcEntityTemplate template = …;

// 1. 初始插入数据 此时version = 0
Mono<Person> daenerys = template.insert(new Person("Daenerys"));            

// 2. 加载刚刚插入的数据,此时加载的version = 0
Person other = template.select(Person.class) .matching(query(where("id").is(daenerys.getId()))).first().block();                                                    

// 3. 更新数据,此处更新后该条数据的version = 1
daenerys.setLastname("Targaryen");
template.update(daenerys);                                                            

// 4. 更新数据,由于other中的version = 0 ;而数据库已经是1了,所以这里会触发OptimisticLockingFailureException异常
template.update(other).subscribe();

关于Repository的使用可以参考:《Spring WebFlux入门实例并整合数据库实现基本的增删改查

以上就是完整的代码

完毕!!!

求个关注

Spring WebFlux入门实例并整合数据库实现基本的增删改查

Reactor响应式编程(Flux、Mono)基本用法

异步编程还在用Future?快来学习下CompletableFuture超级详细

SpringBoot使用WebSocket实现即时消息

SpringCloud Gateway 应用Hystrix 限流功能 自定义Filter详解

使用Filebeat收集springboot项目日志到elasticsearch(EFK)

Restful API设计规范

相关推荐

mysql一 基本操作(mysql基础操作命令)

先讲数据库--mysql、redis、MongoDB爬虫今天的内容:mysql一基本操作注意事项:1.安装:自己安装,有问题可以问老师、可以自己找办法解决2.上课:先讲知识点再回答问题3....

香港DM德馬景泰藍文科集團20250702期《生命的收獲》LIN JING DE

香港DM德馬景泰藍文科集團20250702期《生命的收獲》DMSIXSEVEN/LINJINGDE/1973主編供圖:香港DM德馬數字甲骨文集團Hongkong-basedDMDelm...

01.Java发展历史(java发展简史)

1.Java发展历史Java由SunMicrosystems公司(现为Oracle公司)的JamesGosling及其团队在1991年开发,最初命名为"Oak",后改名为"...

月薪最高12000!看看这些宝山岗位有没有你心仪的~

招聘目录信息1猎上网络科技(上海)有限公司职位分析师职位分析师助理2上海祥腾金属材料有限公司商务车驾驶员3上海赋拓物联网技术有限公司软件测试工程师4上海集优标五高强度紧固件有限公司...

69岁的Java之父终于退休了,他竟然也经历过职场PUA

有些程序员不到35岁就要担心被优化,但有的程序员干到了69岁的高龄,才准备退休。就前几天,Java之父詹姆斯·高斯林,自个儿在领英上宣布以后要美美享受退休生活了。这一下子,就炸出了不少...

ocm认证年薪多少?(ocm认证好考吗)

从业数据库方面的工程师待遇都很好,年薪30万都是比较平常的事。OCM认证的价值可以归纳成以下几点:1、可以全面掌握Oracle知识,专业知识完善;2、证书对于公司招投标有巨大作用,甚至可以不上班就拿钱...

自学java开发攻略,想做程序员的必备攻略?

背景现阶段,随着互联网的快速发展,程序员这个行业越来月吃香,不仅仅是因为科技先进,说出去很牛逼,让别人产生崇拜,主要原因是程序员行业薪资待遇好,相比国内其他行业,程序员的薪资待遇是羡慕了很多人呢!于是...

SpringBoot WebFlux整合R2DBC实现数据库反应式编程

环境:Springboot2.4.12R2DBC简介SpringdataR2DBC是更大的Springdata系列的一部分,它使得实现基于R2DBC的存储库变得容易。R2DBC代表反应式关系数...

AI编码工具未能代替码农:大模型的局限性揭秘

随着ChatGPT和GithubCopilot等AI编码工具的兴起,StackOverflow近日因流量减少宣布裁员近三分之一。这引发了一个争议的问题:ChatGPT这类AI编码工具,真的要颠覆整...

大模型无法替代码农!GPT-4解决GitHub编程问题成功率为0

编辑:编辑部【新智元导读】ChatGPT之类的AI编码工具来势汹汹,StackOverflow又裁员了!不过,普林斯顿和芝大竟发现,面对真实世界GitHub问题,GPT-4的解决率竟是0%。Stac...

机器人动作领域突破!UniVLA模型实现跨模态AI机器人动作统一

还记得《星际穿越》中那台能在无重力、尘暴甚至巨浪中切换形态、随时理解库珀指令的TARS多功能机器人吗?在银幕里,我们羡慕TARS能听懂人话、看懂环境、马上给出最合适的动作;而在现实世界的实验室里,“让...

FEA-Bench:首个仓库级新功能实现基准,让大模型更懂软件开发

当前,大语言模型在代码生成领域已展现出惊人的能力,但能否胜任真实软件开发中的“新增功能实现”任务,仍是一个关键未解的问题。对此,微软亚洲研究院与北京大学联合发布了首个专注于仓库级新功能实现的基准测试...

基于Java的软件版本信息管理系统-2025计算机毕业设计

基于Java的软件版本信息管理系统-2025计算机毕业设计前言随着移动互联网的快速发展,APP已成为人们日常生活中不可或缺的一部分。为了高效地管理这些APP的信息,如版本更新、用户反馈、下载量统计等,...

马斯克在干嘛?抱着它现身推特 简介改为“推特老板”

  马斯克收购推特的交易真是“没完没了”。据彭博社报道,当地时间26日,美国亿万富翁埃隆·马斯克抱着水槽走进了推特公司的旧金山总部,他还将自己在推特账号的公开自我介绍改为“推特老板”(ChiefTw...

福布斯公布7月全球十大富豪:马斯克成6月最大“输家”,身家缩水160亿美元

7月日,福布斯今日公布了2025年7月全球十大富豪榜,其中第二名迎来新面孔。软件巨头甲骨文(Oracle)联合创始人兼董事长拉里埃里森(LarryEllison)凭借公司股价飙升成功上位。由...

取消回复欢迎 发表评论: