1. 前言

使用R2DBC操作MySQL数据库 一文中初步介绍了r2dbc-mysql的使用。由于借助DatabaseClient操作MySQL,过于初级和底层,不利于开发。今天就利用Spring Data R2DBC来演示Spring 数据存储抽象(Spring Data Repository)风格的R2DBC数据库操作。

请注意:目前Spring Data R2DBC虽然已经迭代了多个正式版,但是仍然处于初级阶段,还不足以运用到生产中。不过未来可期,值得研究学习。

2. Spring Data R2DBC

Spring Data R2DBC提供了基于R2DBC反应式关系数据库驱动程序的流行的Repository抽象。但是这并不是一个ORM框架,你可以把它看做一个数据库访问的抽象层或者R2DBC的客户端程序。它不提供ORM框架具有的缓存、懒加载等诸多特性,但它抽象了数据库和对象的抽象映射关系,具有轻量级、易用性的特点。

2.1 版本对应关系

胖哥总结了截至目前Spring Data R2DBCSpring Framework的版本对应关系:

Spring Data R2DBC Spring Framework
1.0.0.RELEASE 5.2.2.RELEASE
1.1.0.RELEASE 5.2.6.RELEASE
1.1.1.RELEASE 5.2.7.RELEASE
1.1.2.RELEASE 5.2.8.RELEASE

一定要注意版本对应关系,避免不兼容的情况。

3. 基础依赖

上次我没有引用R2DBC连接池,这次我将尝试使用它。主要依赖如下 ,这里我还集成了Spring Webflux:

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-data-r2dbc</artifactId>
  4. </dependency>
  5. <!-- r2dbc 连接池 -->
  6. <dependency>
  7. <groupId>io.r2dbc</groupId>
  8. <artifactId>r2dbc-pool</artifactId>
  9. </dependency>
  10. <!--r2dbc mysql 库-->
  11. <dependency>
  12. <groupId>dev.miku</groupId>
  13. <artifactId>r2dbc-mysql</artifactId>
  14. </dependency>
  15. <!--自动配置需要引入的一个嵌入式数据库类型对象-->
  16. <dependency>
  17. <groupId>org.springframework.boot</groupId>
  18. <artifactId>spring-boot-starter-data-jdbc</artifactId>
  19. </dependency>
  20. <!-- 反应式web框架 webflux-->
  21. <dependency>
  22. <groupId>org.springframework.boot</groupId>
  23. <artifactId>spring-boot-starter-webflux</artifactId>
  24. </dependency>

这里我采用的是 Spring Boot 2.3.2.RELEASE

4. 配置

上次我们采用的是JavaConfig风格的配置,只需要向Spring IoC注入一个ConnectionFactory。这一次我将尝试在application.yaml中配置R2DBC的必要参数。

  1. spring:
  2. r2dbc:
  3. url: r2dbcs:mysql://127.0.0.1:3306/r2dbc
  4. username: root
  5. password: 123456

以上就是R2DBC的主要配置。特别注意的是spring.r2dbc.url的格式,根据数据库的不同写法是不同的,要看驱动的定义,这一点非常重要。连接池这里使用默认配置即可,不用显式定义。

5. 编写业务代码

接下来就是编写业务代码了。这里我还尝试使用DatabaseClient来执行了DDL语句创建了client_user表,感觉还不错。

  1. @Autowired
  2. DatabaseClient databaseClient;
  3. @Test
  4. void doDDL() {
  5. List<String> ddl = Collections.unmodifiableList(Arrays.asList("drop table if exists client_user;", "create table client_user(user_id varchar(64) not null primary key,nick_name varchar(32),phone_number varchar(16),gender tinyint default 0) charset = utf8mb4;"));
  6. ddl.forEach(sql -> databaseClient.execute(sql)
  7. .fetch()
  8. .rowsUpdated()
  9. .as(StepVerifier::create)
  10. .expectNextCount(1)
  11. .verifyComplete());
  12. }

5.1 声明数据库实体

熟悉Spring Data JPA的同学应该很轻车熟路了。

  1. /**
  2. * the client user type
  3. *
  4. * @author felord.cn
  5. */
  6. @Data
  7. @Table
  8. public class ClientUser implements Serializable {
  9. private static final long serialVersionUID = -558043294043707772L;
  10. @Id
  11. private String userId;
  12. private String nickName;
  13. private String phoneNumber;
  14. private Integer gender;
  15. }

5.2 声明CRUD接口

上面实体类中的@Table注解是有说法的,当我们的操作接口继承的是ReactiveCrudRepository<T, ID> 或者ReactiveSortingRepository<T, ID>时,需要在实体类上使用@Table注解,这也是推荐的用法。

  1. public interface ReactiveClientUserSortingRepository extends ReactiveSortingRepository<ClientUser,String> {
  2. }

当然实体类不使用@Table注解标记时,我们还可以继承R2dbcRepository<T, ID>接口。然后ReactiveClientUserSortingRepository将提供一些操作数据库的方法。

然后Spring Data JPA怎么写,这里也差不多怎么写,但是有些功能现在还没有得到支持,比如上面提到的分页,还有主键策略等。

类似PagingAndSortingRepository<T,ID>的反应式分页功能接口目前还没有实装,会在未来的版本集成进来。

5.3 实际操作

接下来我们就要通过R2DBC实际操作MySQL数据库了。按照我们传统的逻辑写了如下的新增逻辑:

  1. ClientUser clientUser = new ClientUser();
  2. clientUser.setGender(2);
  3. clientUser.setNickName("r2dbc");
  4. clientUser.setPhoneNumber("9527");
  5. clientUser.setUserId("snowflake");
  6. Mono<ClientUser> save = reactiveClientUserSortingRepository.save(clientUser);

结果数据库并没有写入数据。这时因为r2dbc-mysql不能被直接使用,只能由客户端去实现并委托给客户端去操作。

这也是R2DBC的设计原则,R2DBC的目标是最小化SPI平面,目的是消除数据库之间的差异部分,并使得整个数据库完全具有反应式和背压。它主要用作客户端库使用的驱动程序SPI,而不打算直接在应用程序代码中使用。

所以这里我们可以借助于reactor-test测试库去执行一下,改写为:

  1. reactiveClientUserSortingRepository.save(clientUser)
  2. .log()
  3. .as(StepVerifier::create)
  4. .expectNextCount(1)
  5. .verifyComplete();

但是依然不能执行成功,提示update table [client_user]. Row with Id [snowflake] does not exist ,也就是说期望执行的是新增但是实际执行的是更新,由于数据库找不到主键为snowflake的记录就报了错。这里为什么是更新呢?

这时因为实体类在进行新增时会判断主键是否填充,如果没有填充就认为是新数据,采取真正的新增操作,主键需要数据库来自动填充;如果主键存在值则认为是旧数据则调用更新操作。胖哥同Spring Data R2DBC的项目组沟通后并没有得到友好的解决方案,不过我已经找到了方法,这里先留个坑。

那么该如何新增一条数据呢?我们只能借助于@Query注解来编写一条SQL写入了:

  1. @Modifying
  2. @Query("insert into client_user (user_id,nick_name,phone_number,gender) values (:userId,:nickName,:phoneNumber,:gender)")
  3. Mono<Integer> addClientUser(String userId, String nickName, String phoneNumber, Integer gender);

当添加了@Modifying后,返回值可以从Mono<ClientUser>Mono<Boolean>或者Mono<Integer>任意一种选择。

  1. reactiveClientUserSortingRepository
  2. .addClientUser("snowflake",
  3. "r2dbc",
  4. "132****155",
  5. 0)
  6. .as(StepVerifier::create)
  7. .expectNextCount(1)
  8. .verifyComplete();

这样就证明写成功了一条数据。

5.4 搭配Webflux使用

但是实际中该如何应用呢?目前能够想到的就是结合反应式框架Spring Webflux了,就像Spring Data JPA配合Spring MVC一样。

我们编写一个Webflux接口:

  1. @RestController
  2. @RequestMapping("/user")
  3. public class ReactiveClientUserController {
  4. @Autowired
  5. private ReactiveClientUserSortingRepository reactiveClientUserSortingRepository;
  6. /**
  7. * 这里为了检验默认api 就不分层了
  8. *
  9. * @param userId the user id
  10. * @return the mono
  11. */
  12. @GetMapping("/{userId}")
  13. public Mono<ClientUser> findUserById(@PathVariable String userId) {
  14. return reactiveClientUserSortingRepository.findById(userId);
  15. }
  16. }

5.5 一些测试数据参考

在低并发时,Spring MVC + JDBC表现最佳,但在高并发下,WebFlux + R2DBC使用每个已处理请求的内存最少。

在高并发下,Spring MVC + JDBC的响应时间开始下降。显然,R2DBC在更高的并发性下提供了更好的响应时间。Spring WebFlux也比使用Spring MVC的类似实现更好。

6. 总结

今天对Spring Data R2DBC进一步演示,相信你能够从中学到一些东西。由于R2DBC还是比较新,还存在一些需要改进和补充的东西。目前社区非常活跃,发展十分迅速。好了今天的文章就到这里,原创不易多多关注:码农小胖哥 如果你觉得本文很有用,请点赞、转发、再看。

关注公众号:Felordcn 获取更多资讯

个人博客:https://felord.cn

Spring Data R2DBC响应式操作MySQL的更多相关文章

  1. .NET 使用 MySql.Data.dll 动态库操作MySql的帮助类--MySqlHelper

    .NET 使用 MySql.Data.dll 动态库操作MySql的帮助类--MySqlHelper 參考演示样例代码,例如以下所看到的: /// <summary> /// MySql ...

  2. 【spring data jpa】使用spring data jpa 的删除操作,需要加注解@Modifying @Transactional 否则报错如下: No EntityManager with actual transaction available for current thread - cannot reliably process 'remove' call

    使用spring data jpa 的删除操作,需要加注解@Modifying     @Transactional 否则报错如下: No EntityManager with actual tran ...

  3. 实战SpringCloud响应式微服务系列教程(第九章)使用Spring WebFlux构建响应式RESTful服务

    本文为实战SpringCloud响应式微服务系列教程第九章,讲解使用Spring WebFlux构建响应式RESTful服务.建议没有之前基础的童鞋,先看之前的章节,章节目录放在文末. 从本节开始我们 ...

  4. 浅谈Spring 5的响应式编程

    这篇使用Spring 5进行响应式编程的入门文章展示了你现在可以使用的一些新的non-blocking, asynchronous.感谢优锐课老师给予的指导! 近年来,由于响应式编程能够以声明性的方式 ...

  5. spring data jpa使用懒操作

    如果model对象的某属性使用lazy load,调用这个属性时会报错, failed to lazily initialize a collection of role could not init ...

  6. 看Spring Data如何简化数据操作

    Spring Data 概述 Spring Data 用于简化数据库访问,支持NoSQL 和 关系数据存储,其主要目标是使数据库的访问变得方便快捷. SpringData 项目所支持 NoSQL 存储 ...

  7. spring boot ----> jpa连接和操作mysql数据库

    环境: centos6.8,jdk1.8.0_172,maven3.5.4,vim,spring boot 1.5.13,mysql-5.7.23 1.引入jpa起步依赖和mysql驱动jar包 &l ...

  8. php实现简单链式操作mysql数据库类

    <?php $dbConfig = require_once(dirname(__FILE__).'/config.php'); class Db{     public $conn;      ...

  9. spring data jpa的update操作

    简介 使用jpa进行update操作主要有两种方式: 1.调用保存实体的方法 1)保存一个实体:repository.save(T entity) 2)保存多个实体:repository.save(I ...

随机推荐

  1. 化繁就简,如何利用Spring AOP快速实现系统日志

    1.引言 有关Spring AOP的概念就不细讲了,网上这样的文章一大堆,要讲我也不会比别人讲得更好,所以就不啰嗦了. 为什么要用Spring AOP呢?少写代码.专注自身业务逻辑实现(关注本身的业务 ...

  2. 什么是jsp?

    1.什么是jsp? jsp就是java 服务器页面(java server page) 2.jsp有什么用? jsp的出现是为了解决Servlet页面显示方面的不足. 3.jsp的三种脚本: 4.js ...

  3. C# wpf 实现 MD5加密解密 小工具

    源文件: http://pan.baidu.com/share/link?shareid=2038099474&uk=3912660076 MD5 C# 实现代码来源于网络,感谢原系作者! 参 ...

  4. 利用搭载好的工控机环境跑yolov3-tiny

    辛辛苦苦搭载好GPU环境现在要开始测试下效果 1,准备好数据集 2,测试开始 (1),如果尚未安装Darknet,则应先进行安装 git clone https://github.com/pjredd ...

  5. java语言进阶(七)_Lambda表达式

    1 函数式编程思想概述 在数学中,函数就是有输入量.输出量的一套计算方案,也就是"拿什么东西做什么事情".相对而言,面向对象过分强调"必须通过对象的形式来做事情" ...

  6. 1、Pycharm中创建项目自动生成头信息

    1.打开PyCharm 选择File-Settings 2.模块变量描述,并且重新创建一个.py的文件测试,检测是否自动生成头信息

  7. Selenium之浏览器驱动下载和配置使用

    浏览器驱动下载 Chrome浏览器驱动:chromedriver , taobao备用地址 Firefox浏览器驱动:geckodriver Edge浏览器驱动:MicrosoftWebDriver ...

  8. 图片文件分布式存储方案设计模式(c#--sqlserver)

    1.为了降低web服务器的压力,申请了2台文件服务器,用来存放图片文件.但是两台文件服务器如何让程序自己选择呢? 于是我用了一个算法,思路如下: 从状态表筛选出可用的图片服务器集合记作C,并获取集合的 ...

  9. 生日聚会Party——这个线性dp有点嚣张

    题目描述 今天是hidadz小朋友的生日,她邀请了许多朋友来参加她的生日party. hidadz带着朋友们来到花园中,打算 坐成一排玩游戏.为了游戏不至于无聊,就座的方案应满足如下条件:对于任意连续 ...

  10. 重学c#系列——对c#粗浅的认识(一)

    前言 什么是c#呢? 首先你是如何读c#的呢?c sharp?或者c 井? 官方读法是:see sharp. 有没有发现开发多年,然后感觉名字不对. tip:为个人重新整理,如学习还是看官网,c# 文 ...