响应式编程库(三) -r2dbc
r2dbc整合
- 什么是r2dbc
- 版本选择
- 简单试用
- 整合springboot
- DatabaseClient 进行查询
- 使用Repository接口(对应mapper)
- 实体类
- 复杂查询(一对一)实体类转换器
- 测试代码
- 一对多关系
什么是r2dbc
反应式关系数据库连接(R2DBC)项目为关系数据库带来了反应式编程API。
基于Reactive Streams
规范。R2DBC建立在Reactive Streams规范之上,它提供了一个完全反应式的非阻塞API
。
r2dbc 官网:https://r2dbc.io/
github: r2dbc-mysql 版本
spring-data r2dbc
版本选择
参考下表来确定适合你的项目的r2 dbc-mysql版本。
spring-boot-starter-data-r2dbc | spring-data-r2dbc | r2dbc-spi | r2dbc-mysql(recommended) |
---|---|---|---|
3.0.* and above | 3.0.* and above | 1.0.0.RELEASE | io.asyncer:r2dbc-mysql:1.2.0 |
2.7.* | 1.5.* | 0.9.1.RELEASE | io.asyncer:r2dbc-mysql:0.9.7 |
2.6.* and below | 1.4.* and below | 0.8.6.RELEASE | dev.miku :r2dbc-mysql:0.8.2 |
简单试用
<!-- https://mvnrepository.com/artifact/dev.miku/r2dbc-mysql --><dependency><groupId>dev.miku</groupId><artifactId>r2dbc-mysql</artifactId><version>0.8.2.RELEASE</version></dependency>
@Testvoid connection() throws IOException {// r2dbc基于全异步、响应式、消息驱动// jdbc:mysql://localhost:3306/test// r2dbc:mysql://localhost:3306/test//0、MySQL配置MySqlConnectionConfiguration configuration = MySqlConnectionConfiguration.builder().host("192.168.xx.xx").port(3306).username("root").password("123456").database("test").connectTimeout(Duration.ofSeconds(3)).build();//1、获取连接工厂MySqlConnectionFactory connectionFactory = MySqlConnectionFactory.from(configuration);//2、获取到连接,发送sqlMono<Connection> connectionMono = Mono.from(connectionFactory.create());// JDBC: Statement: 封装sql的//3、数据发布者connectionMono.flatMapMany(connection ->connection
// .createStatement("INSERT INTO `t_book` (`publisher`, `author`) VALUES ('who', '1')").createStatement("select * from t_book where id=?id and publisher=?").bind("id", 1L) //具名参数.bind(1, "pub").execute()).flatMap(result -> {// 不同版本,api有所不一致return result.map((readable,book)->{System.out.println("readable:"+readable);System.out.println("book:"+book);Long id = readable.get("id", Long.class);String publisher = readable.get("publisher", String.class);Long author = readable.get("author", Long.class);return new TBook(author,publisher,id);});}).subscribe(tAuthor -> System.out.println("book = " + tAuthor));//背压; 不用返回所有东西,基于请求量返回;System.in.read();}
结果:
readable:dev.miku.r2dbc.mysql.MySqlRow@34579a88
book:MySqlRowMetadata{metadata=[MySqlColumnMetadata{index=0, type=8, name='id', definitions=4203, nullability=NON_NULL, size=20, decimals=0, collationId=63}, MySqlColumnMetadata{index=1, type=253, name='publisher', definitions=1001, nullability=NON_NULL, size=1020, decimals=0, collationId=45}, MySqlColumnMetadata{index=2, type=3, name='author_id', definitions=1001, nullability=NON_NULL, size=11, decimals=0, collationId=63}, MySqlColumnMetadata{index=3, type=12, name='create_time', definitions=81, nullability=NON_NULL, size=19, decimals=0, collationId=63}], sortedNames=[author_id, create_time, id, publisher]}
book = TBook(authorId=1, publisher=pub, id=1)
整合springboot
<!-- https://mvnrepository.com/artifact/io.asyncer/r2dbc-mysql --><dependency><groupId>io.asyncer</groupId><artifactId>r2dbc-mysql</artifactId><version>1.0.5</version></dependency><!-- 响应式 Spring Data R2dbc--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-r2dbc</artifactId></dependency>
DatabaseClient 进行查询
@Autowired //贴近底层,join操作好做; 复杂查询好用DatabaseClient databaseClient; //数据库客户端@Testvoid databaseClient() throws IOException {// 底层操作databaseClient.sql("select * from t_author")
// .bind(0,2L).fetch() //抓取数据.all()//返回所有.map(map -> { //map == bean 属性=值System.out.println("map = " + map);String id = map.get("id").toString();String name = map.get("name").toString();return new TAuthor(Long.parseLong(id), name, null);}).subscribe(tAuthor -> System.out.println("tAuthor = " + tAuthor));System.in.read();}
spring:r2dbc:password: 123456username: rooturl: r2dbc:mysql://localhost:3306/testname: test
使用Repository接口(对应mapper)
/**
* TAuthor : 对应实体类; Long 主键类型
*/
@Repository
public interface AuthorRepositories extends R2dbcRepository<TAuthor,Long> {//默认继承了一堆CRUD方法; 像mybatis-plus//QBC: Query By Criteria//QBE: Query By Example//成为一个起名工程师 where id In () and name like ?//仅限单表复杂条件查询。 不用编写sql!!!根据方法名自动生成sqlFlux<TAuthor> findAllByIdInAndNameLike(Collection<Long> id, String name);//多表复杂查询@Query("select * from t_author") //自定义query注解,指定sql语句Flux<TAuthor> findHaha();// 1-1关联关系; 查出这本图书以及它的作者@Query("select b.*,t.name as name from t_book b" +" LEFT JOIN t_author t on b.author_id = t.id " +" WHERE b.id = :bookId")Mono<TBookAuthor> authorBook(@Param("bookId") Long bookId);// @Query("SELECT * FROM person WHERE lastname = :lastname")
// Flux<Person> findByLastname(String lastname);
//
// @Query("SELECT firstname, lastname FROM person WHERE lastname = $1")
// Mono<Person> findFirstByLastname(String lastname);}
实体类
@Data
@AllArgsConstructor
@NoArgsConstructor@Table("t_book")
public class TBook {Long authorId;String publisher;@IdLong id;}
@Table("t_author")
@NoArgsConstructor
@AllArgsConstructor
@Data
public class TAuthor {@Idprivate Long id;private String name;//1-N如何封装@Transient //临时字段,并不是数据库表中的一个字段
// @Field(exist=false)private List<TBook> TBooks;
}
@Table("t_book")
@Data
public class TBookAuthor {@IdLong id;Long authorId;String publisher;/*** 响应式中日期的映射用 Instant 或者 LocalXxx*/Instant createTime;/*** 一对一 关系 实体类*/TAuthor TAuthor;}
复杂查询(一对一)实体类转换器
springdata r2dbc mapping 文档
/*** 告诉Spring Data 怎么封装 TBookAuthor 对象*/
@ReadingConverter
public class BookAuthorConverter implements Converter<Row, TBookAuthor> {//1)、@Query 指定了 sql如何发送//2)、自定义 BookConverter 指定了 数据库返回的一 Row 数据,怎么封装成 TBook//3)、配置 R2dbcCustomConversions 组件,让 BookConverter 加入其中生效@Overridepublic TBookAuthor convert(Row source) {if(source == null) return null;//自定义结果集的封装TBookAuthor tBook = new TBookAuthor();tBook.setId(source.get("id", Long.class));tBook.setPublisher(source.get("publisher", String.class));Long author_id = source.get("author_id", Long.class);tBook.setAuthorId(author_id);tBook.setCreateTime(source.get("create_time", Instant.class));//让 converter兼容更多的表结构处理
// if (source.get("name",String.class)) {TAuthor tAuthor = new TAuthor();tAuthor.setId(author_id);tAuthor.setName(source.get("name", String.class));tBook.setTAuthor(tAuthor);
// }return tBook;}
}
注册转换器
@EnableR2dbcRepositories //开启 R2dbc 仓库功能;jpa
@Configuration
public class R2DbcConfiguration {@Bean //替换容器中原来的@ConditionalOnMissingBeanpublic R2dbcCustomConversions conversions(){//把我们的转换器加入进去; 效果新增了我们的 Converterreturn R2dbcCustomConversions.of(MySqlDialect.INSTANCE,new BookAuthorConverter());}
}
测试代码
@SpringBootTest
public class AppTest {@AutowiredTBookMapper bookMapper;@Testvoid testCRUD() throws IOException {
// bookMapper.findAll().subscribe(System.out::println);
// bookMapper.findById(1L).subscribe(System.out::println);TBookAuthor block = bookMapper.authorBook(1L).block();System.out.println(block);//查询是全异步的, 需要阻塞一下System.in.read();}}
一对多关系
@Testvoid oneToN() throws IOException {// databaseClient.sql("select a.id aid,a.name,b.* from t_author a " +
// "left join t_book b on a.id = b.author_id " +
// "order by a.id")
// .fetch()
// .all(row -> {
//
// })// 1~6// 1:false 2:false 3:false 4: true 8:true 5:false 6:false 7:false 8:true 9:false 10:false// [1,2,3]// [4,8]// [5,6,7]// [8]// [9,10]// bufferUntilChanged:// 如果下一个判定值比起上一个发生了变化就开一个新buffer保存,如果没有变化就保存到原buffer中// Flux.just(1,2,3,4,8,5,6,7,8,9,10)
// .bufferUntilChanged(integer -> integer%4==0 )
// .subscribe(list-> System.out.println("list = " + list));; //自带分组Flux<TAuthor> flux = databaseClient.sql("select a.id aid,a.name,b.* from t_author a " +"left join t_book b on a.id = b.author_id " +"order by a.id").fetch().all().bufferUntilChanged(rowMap -> Long.parseLong(rowMap.get("aid").toString())).map(list -> {TAuthor tAuthor = new TAuthor();Map<String, Object> map = list.get(0);tAuthor.setId(Long.parseLong(map.get("aid").toString()));tAuthor.setName(map.get("name").toString());//查到的所有图书List<TBook> tBooks = list.stream().map(ele -> {TBook tBook = new TBook();tBook.setId(Long.parseLong(ele.get("id").toString()));tBook.setAuthorId(Long.parseLong(ele.get("author_id").toString()));tBook.setTitle(ele.get("title").toString());return tBook;}).collect(Collectors.toList());tAuthor.setBooks(tBooks);return tAuthor;});//Long 数字缓存 -127 - 127;// 对象比较需要自己写好equals方法flux.subscribe(tAuthor -> System.out.println("tAuthor = " + tAuthor));System.in.read();}
相关文章:

响应式编程库(三) -r2dbc
r2dbc整合 什么是r2dbc版本选择简单试用整合springbootDatabaseClient 进行查询使用Repository接口(对应mapper)实体类复杂查询(一对一)实体类转换器测试代码一对多关系 什么是r2dbc 反应式关系数据库连接(R2DBC)项目为关系数据库…...

嵌入式AI革命:DeepSeek开源如何终结GPU霸权,开启单片机智能新时代?
2025年,全球AI领域最震撼的突破并非来自算力堆叠的超级模型,而是中国团队DeepSeek通过开源策略,推动大模型向微型化、低功耗场景的跨越。相对于当人们还在讨论千亿参数模型的训练成本被压缩到600万美金而言,被称作“核弹级别”的操…...

基于遗传算法的64QAM星座图的最优概率整形matlab仿真,对比优化前后整形星座图和误码率
目录 1.算法仿真效果 2.算法涉及理论知识概要 3.MATLAB核心程序 4.完整算法代码文件获得 1.算法仿真效果 matlab2022a仿真结果如下(完整代码运行后无水印): GA优化过程: 优化前后星座图对比: (优化后…...

从零开始玩转Docker:轻松开启容器化之旅
一、什么是 Docker Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。简单来说,Docker 就像是一个超级 “快递箱”,…...

kafka生产端之架构及工作原理
文章目录 整体架构元数据更新 整体架构 消息在真正发往Kafka之前,有可能需要经历拦截器(Interceptor)、序列化器(Serializer)和分区器(Partitioner)等一系列的作用,那么在此之后又会…...

38、【OS】【Nuttx】OSTest分析(3):参数传递
背景 接之前 blog 36、【OS】【Nuttx】OSTest分析(2):环境变量测试 37、【OS】【Nuttx】OSTest分析(2):任务创建 分析完环境变量测试,和任务创建的一些关键要素,OSTest 进入下一阶段…...

存储异常导致的Oracle重大生产故障
📢📢📢📣📣📣 作者:IT邦德 中国DBA联盟(ACDU)成员,10余年DBA工作经验 Oracle、PostgreSQL ACE CSDN博客专家及B站知名UP主,全网粉丝10万 擅长主流Oracle、MySQL、PG、高斯…...

C语言时间相关宏定义
在C语言中,预处理器提供了一些与时间相关的宏定义,用于在编译时获取日期、时间等信息。除了 __TIMESTAMP__ 和 __DATE__,还有以下相关的宏定义: __DATE__ 当前编译日期的字符串,格式为 "Mmm dd yyyy"&#x…...

Android Studio:Application 和 Activity的区别
Application 和 Activity 是 Android 中非常重要的两个组件,它们分别负责不同的生命周期管理和应用的不同层次的操作。 Application 是应用级别的生命周期管理,它在整个应用运行时只有一个实例,负责应用的全局初始化和资源管理。Activity 是…...

如何优化爬虫以提高搜索效率
在数据采集和网络爬虫领域,优化爬虫性能是提升数据采集效率的关键。随着网页结构的日益复杂和数据量的不断增长,高效的爬虫能够显著降低运行时间和资源成本。本文将详细介绍如何优化爬虫以提高搜索效率,包括选择合适的工具、优化代码逻辑、使…...

git撤销上一次的提交
1、撤销提交 如果需要撤销上一次的提交,只是提交到了本地,可以通过命令: // 撤销最近的提交(保留修改) git reset --soft HEAD~1 这个操作可以保留之前的提交和当前的修改。最近一次的提交到本地的修改的提交会回到…...

LLM学习笔记1——本地部署Meta-Llama-3.2-1B大模型
系列文章目录 参考博客 参考博客 文章目录 系列文章目录前言与调用一、部署要求二、实现步骤0.深度学习环境错误1,验证pytorch版本时提示以下问题:错误2,验证pytorch版本时提示以下问题:错误3,有时候还会提示你有一些…...

Nginx反代Ollama接口跨域、无法逐字输出问题
场景 本地部署deepseek模型,用的Ollama管理,内网穿透到公网,在通过nginx反代ollama接口。 问题描述 跨域问题 nginx转发时请求头中需要加入origin,并且origin还要和ollama接口同源(协议、ip、端口一致)。…...

大学资产管理系统中的下载功能设计与实现
大学资产管理系统是高校信息化建设的重要组成部分,它负责记录和管理学校内所有固定资产的信息。随着信息技术的发展,下载功能成为提高资产管理效率的关键环节之一。 系统架构的设计是实现下载功能的基础。一个良好的系统架构能够确保数据的高效传输和存储…...

股指入门:股指期货是什么意思?在哪里可以做股指期货交易?
股指期货是一种以股票指数为标的物的期货合约,也可以称为股票指数期货或期指。 股指期货是什么意思? 股指期货是一种金融衍生品,其标的资产是股票市场上的股指,例如标普500指数、道琼斯工业平均指数、上证50指数等。 股指期货允…...

< OS 有关 > 利用 google-drive-ocamlfuse 工具,在 Ubuntu 24 系统上 加载 Google DRIVE 网盘
Created by Dave On 8Feb.2025 起因: 想下载 StableDiffusion,清理系统文件时把 i/o 搞到 100%,已经删除到 apt 缓存,还差 89MB,只能另想办法。 在网上找能不能挂在 Google 网盘,百度网盘,或 …...

Golang的引用类型和指针
在Golang中,引用类型和指针是两个容易混淆的概念,但它们有本质的区别。理解它们的区别对于编写高效、正确的Go代码至关重要。 1. 引用类型 引用类型是Go语言中某些内置类型的统称,它们的值在传递时共享底层数据,而不是复制数据。…...

51单片机之冯·诺依曼结构
一、概述 8051系列单片机将作为控制应用最基本的内容集成在一个硅片上,其内部结构如图4-1所示。作为单一芯片的计算机,它的内部结构与一台计算机的主机非常相似。其中微处理器相当于计算机中的CPU,由运算器和控制器两个部分构成;…...

32. C 语言 安全函数( _s 尾缀)
本章目录 前言什么是安全函数?安全函数的特点主要的安全函数1. 字符串操作安全函数2. 格式化输出安全函数3. 内存操作安全函数4. 其他常用安全函数 安全函数实例示例 1:strcpy_s 和 strcat_s示例 2:memcpy_s示例 3:strtok_s 总结 …...

Android T(13) 源码分析 — BufferQueue 的分析
Android T(13) 源码分析 — BufferQueue 的分析 文章目录 Android T(13) 源码分析 — BufferQueue 的分析前言摘要一、Java 层的 BufferQueue 分析二、原生层的 BufferQueue 分析1、BLASTBufferQueue 的创建2、BLASTBufferQueue 的更新3、Surface 的创建 总结 前言 该系列文章…...

Vite+TS项目中配置路径别名
在使用 Vite 和 TypeScript 的项目中配置路径别名,可以简化模块导入路径,提高代码的可读性和维护性。以下是详细的步骤和示例代码: 1. 配置 Vite 别名 前置条件 下载types/node 下面引入的path会用到 npm install types/node --save-dev原…...

看盘细节系列 篇二:集合竞价的9点18分大单打到3%以下或以上,9点19分撤单
文章目录 系列文章现象原因分析时间点含义正常情况测试市场反应诱导跟风操纵股价意图系列文章 看盘细节系列 篇一:集合竞价尾盘突变 现象 集合竞价中 9 点 18 分通过一笔大单或连续几笔大单将股价打到 3% 以下或以上,9 点 19 分又迅速撤单。从而在分时图上留下一根长长的上…...

Java继承简介
继承的本质:是代码的复用,重复使用已经定义好的方法和域(即全局变量) 要掌握继承首先要了解Java方法的重载和重写 方法的重载和重写 方法的重载 当前方法名相同,但是参数类型不同,发生重载 类比数学函…...

redis之哨兵集群搭建
一:哨兵集群工作概览图 1.监控:sentinel通过心跳监控redis的master和slave实例是否正常工作 2.故障转移:假如master出现故障,sentinel会选举一个slave作为新的master,当故障实例恢复后身份会变成slave,会以…...

保姆级AI开发环境搭建
目录 windows下环境搭建1. Python环境搭建2. 下载vLLM2.1 安装CUDA2.2 安装Pytorch2.3 安装vllm 3. 部署Deepseek(huggingface)3.1 DeepSeek的优化建议 4. ollama快速部署Deepseek4.1 下载Ollama4.2 配置Ollma4.2 运行模型4.3 其他Ollama命令 linux下环境…...

Arduino 型号的对比
常见 Arduino 型号的对比表格 涵盖了不同型号的关键参数和特点,方便你根据项目需求进行选择: 型号Arduino UnoArduino Mega 2560Arduino LeonardoArduino NanoArduino Due微控制器ATmega328PATmega2560ATmega32U4ATmega328P 或 ATmega168SAM3X8E&#…...

Kafka系列之:定位topic只能保存最新数据的原因
Kafka系列之:定位topic只能保存最新数据的原因 一、背景二、定位排查方向三、深入排查一、背景 kafka topic保存的数据少,topic只能保存最新的数据二、定位排查方向 能想到的定位排查方向:topic能存储的数据量、topic数据保存的时间、topic数据大小./bin/kafka-configs.sh -…...

AtCoder Beginner Contest 391(A~E题题解)
A - Lucky Direction 思路:纯模拟的一个水题 #include <bits/stdc.h> using namespace std; #define int long long string s; signed main() { cin>>s;for(int i0;i<s.size();i){char cs[i];if(cN){cout<<"S";}else if(c…...

mysql mvcc 锁 关系
多版本并发控制(MVCC)是一种用于数据库并发控制的机制,它可以在保证数据一致性的同时,提高数据库的并发性能。下面结合 MVCC 机制,详细阐述常见的四种事务隔离级别(读未提交、读已提交、可重复读、串行化&a…...

安卓手机基于 Termux 安装 AList 并设置开机自启的详细教程
安装 AList 安装 Termux: 点击下载 更新软件包:打开 Termux,运行以下命令以更新软件包列表并升级已安装的软件包: bash复制 pkg update && pkg upgrade安装 AList:运行以下命令安装 AList: bash复…...