响应式编程库(三) -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-r2dbcspring-data-r2dbcr2dbc-spir2dbc-mysql(recommended)
3.0.* and above3.0.* and above1.0.0.RELEASEio.asyncer:r2dbc-mysql:1.2.0
2.7.*1.5.*0.9.1.RELEASEio.asyncer:r2dbc-mysql:0.9.7
2.6.* and below1.4.* and below0.8.6.RELEASEdev.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>

 @Test
    void 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、获取到连接,发送sql
        Mono<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; //数据库客户端
 @Test
    void 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: 123456
    username: root
    url: r2dbc:mysql://localhost:3306/test
    name: 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!!!根据方法名自动生成sql
    Flux<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;

    
    @Id
    Long id;
 }
@Table("t_author")
@NoArgsConstructor
@AllArgsConstructor
@Data
public class TAuthor {

    @Id
    private Long id;
    private String name;

    //1-N如何封装
    @Transient //临时字段,并不是数据库表中的一个字段
//    @Field(exist=false)
    private List<TBook> TBooks;
}

@Table("t_book")
@Data
public class TBookAuthor {
    
    @Id
    Long 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 加入其中生效
    @Override
    public 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 //替换容器中原来的
    @ConditionalOnMissingBean
    public R2dbcCustomConversions conversions(){

        //把我们的转换器加入进去; 效果新增了我们的 Converter
        return R2dbcCustomConversions.of(MySqlDialect.INSTANCE,new BookAuthorConverter());
    }
}

测试代码

@SpringBootTest
public class AppTest {

    @Autowired
    TBookMapper bookMapper;

    @Test
    void 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();


    }
 }

一对多关系

在这里插入图片描述

    @Test
    void 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();


    }

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

第26场蓝桥入门赛

5.扑克较量【算法赛】 - 蓝桥云课 C&#xff1a; #include <iostream> #include <algorithm> using namespace std;int a[100005];int main() {int n,k;cin>>n>>k;for (int i1; i<n; i)cin>>a[i], a[i] % k;sort(a1, a1n);int mx a[1]k-a…

封装descriptions组件,描述,灵活

效果 1、组件1&#xff0c;dade-descriptions.vue <template><table><tbody><slot></slot></tbody> </table> </template><script> </script><style scoped>table {width: 100%;border-collapse: coll…

【专题】2025年我国机器人产业发展形势展望:人形机器人量产及商业化关键挑战报告汇总PDF洞察(附原数据表)

原文链接&#xff1a;https://tecdat.cn/?p39668 机器人已广泛融入我们生活的方方面面。在工业领域&#xff0c;它们宛如不知疲倦的工匠&#xff0c;精准地完成打磨、焊接等精细工作&#xff0c;极大提升了生产效率和产品质量&#xff1b;在日常生活里&#xff0c;它们是贴心…

能够从优秀工控app中学到什么?

美的工控类APP设计在多个方面都有值得学习的地方&#xff0c;包括用户体验、视觉设计、功能布局等&#xff0c;以下是具体内容&#xff1a; 用户体验方面 精准的用户需求洞察&#xff1a;工控类APP的目标用户通常是专业的工程师、技术人员等&#xff0c;其设计会深入了解这些…

334递增的三元子序列贪心算法(思路解析+源码)

文章目录 题目思路解析源码总结题目 思路解析 有两种解法:解法一:动态规划(利用dp找到数组最长递增序列长度,判断是否大于3即可)本题不适用,因为时间复杂度为O(n^2),超时。 解法二:贪心算法:解法如上图,题目要求长度为三,设置第一个元素为长度1的值,是指长度二的…

深入探究 C++17 std::is_invocable

文章目录 一、引言二、std::is_invocable 概述代码示例输出结果 三、std::is_invocable 的工作原理简化实现示例 四、std::is_invocable 的相关变体1. std::is_invocable_r2. std::is_nothrow_invocable 和 std::is_nothrow_invocable_r 五、使用场景1. 模板元编程2. 泛型算法 …

P1049 装箱问题(dp)

#include<bits/stdc.h> using namespace std;int main() {int v,n;cin>>v>>n;int a[30];int dp[20005];for(int i0;i<n;i){cin>>a[i];}memset(dp,0,sizeof(dp));// 设置所有元素为0&#xff0c;表示最大体积为0for(int i0;i<n;i){for(int jv;j&…

Groovy基础

引言&#xff1a; Groovy 是一种基于 Java 平台的动态编程语言&#xff08;指在运行时进行类型检查的语言。在使用动态语言编写程序时&#xff0c;变量的类型不需要在声明时明确指定&#xff0c;而是在运行时根据赋给变量的值来确定类型。动态语言在代码执行过程中会进行类型检…

Flink CDC YAML:面向数据集成的 API 设计

摘要&#xff1a;本文整理自阿里云智能集团 、Flink PMC Member & Committer 徐榜江&#xff08;雪尽&#xff09;老师在 Flink Forward Asia 2024 数据集成&#xff08;一&#xff09;专场中的分享。主要分为以下四个方面&#xff1a; Flink CDC YAML API Transform A…

OpenCV:视频背景减除

目录 简述 1. MOG &#x1f537;1.1 主要特点 &#x1f537;1.2 代码示例 &#x1f537;1.3 运行效果 2. MOG2 &#x1f537;2.1 主要特点 &#x1f537;2.2 代码示例 &#x1f537;2.3 运行效果 3. KNN 4. GMG 5. CNT 6. LSBP 7. 如何选择适合的接口&#xff…

PAT乙级( 1009 说反话 1010 一元多项式求导)C语言版本超详细解析

1009 说反话 给定一句英语&#xff0c;要求你编写程序&#xff0c;将句中所有单词的顺序颠倒输出。 输入格式&#xff1a; 测试输入包含一个测试用例&#xff0c;在一行内给出总长度不超过 80的字符串。字符串由若干单词和若干空格组成&#xff0c;其中单词是由英文字母&#x…

OpenCV2D 特征框架 (19)目标检测类cv::CascadeClassifier的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 cv::CascadeClassifier 是 OpenCV 中用于对象检测的一个核心类&#xff0c;特别适用于基于 Haar 特征和 LBP&#xff08;局部二进制模式&#xf…

大数据学习之SparkSql

95.SPARKSQL_简介 网址&#xff1a; https://spark.apache.org/sql/ Spark SQL 是 Spark 的一个模块&#xff0c;用于处理 结构化的数据 。 SparkSQL 特点 1 易整合 无缝的整合了 SQL 查询和 Spark 编程&#xff0c;随时用 SQL 或 DataFrame API 处理结构化数据。并且支…

RabbitMQ 从入门到精通:从工作模式到集群部署实战(四)

#作者&#xff1a;闫乾苓 系列前几篇&#xff1a; 《RabbitMQ 从入门到精通&#xff1a;从工作模式到集群部署实战&#xff08;一&#xff09;》&#xff1a;link 《RabbitMQ 从入门到精通&#xff1a;从工作模式到集群部署实战&#xff08;二&#xff09;》&#xff1a; lin…

ip地址是手机号地址还是手机地址

在数字化生活的浪潮中&#xff0c;IP地址、手机号和手机地址这三个概念如影随形&#xff0c;它们各自承载着网络世界的独特功能&#xff0c;却又因名称和功能的相似性而时常被混淆。尤其是“IP地址”这一术语&#xff0c;经常被错误地与手机号地址或手机地址划上等号。本文旨在…

Django开发入门 – 0.Django基本介绍

Django开发入门 – 0.Django基本介绍 A Brief Introduction to django By JacksonML 1. Django简介 1) 什么是Django? 依据其官网的一段解释&#xff1a; Django is a high-level Python web framework that encourages rapid development and clean, pragmatic design. …

深度解析DeepSeek模型系列:从轻量级到超大规模(附DeepSeek硬件配置清单)

在人工智能领域&#xff0c;深度学习模型的选择对于任务的执行效率和精度至关重要。DeepSeek模型系列提供了多种不同参数量的版本&#xff0c;以满足不同场景下的需求。本文将详细解析DeepSeek模型系列的特点、适用场景以及硬件需求。 DeepSeek模型系列概览 DeepSeek模型系列…

树和二叉树_7

树和二叉树_7 一、leetcode-102二、题解1.引库2.代码 一、leetcode-102 二叉树的层序遍历 给你二叉树的根节点 root &#xff0c;返回其节点值的 层序遍历 。 &#xff08;即逐层地&#xff0c;从左到右访问所有节点&#xff09;。 样例输入&#xff1a;root [3,9,20,null,nu…

【DeepSeek】私有化本地部署图文(Win+Mac)

目录 一、DeepSeek本地部署【Windows】 1、安装Ollama 2、配置环境变量 3、下载模型 4、使用示例 a、直接访问 b、chatbox网页访问 二、DeepSeek本地部署【Mac】 1、安装Ollama 2、配置环境变量 3、下载模型 4、使用示例 5、删除已下载的模型 三、DeepSeek其他 …

<tauri><rust><GUI>基于rust和tauri,在已有的前端框架上手动集成tauri示例

前言 本文是基于rust和tauri&#xff0c;由于tauri是前、后端结合的GUI框架&#xff0c;既可以直接生成包含前端代码的文件&#xff0c;也可以在已有的前端项目上集成tauri框架&#xff0c;将前端页面化为桌面GUI。 环境配置 系统&#xff1a;windows 10 平台&#xff1a;visu…