Spring Data R2DBC响应式操作MySQL
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 R2DBC和Spring 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:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-r2dbc</artifactId>
</dependency>
<!-- r2dbc 连接池 -->
<dependency>
<groupId>io.r2dbc</groupId>
<artifactId>r2dbc-pool</artifactId>
</dependency>
<!--r2dbc mysql 库-->
<dependency>
<groupId>dev.miku</groupId>
<artifactId>r2dbc-mysql</artifactId>
</dependency>
<!--自动配置需要引入的一个嵌入式数据库类型对象-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<!-- 反应式web框架 webflux-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
这里我采用的是 Spring Boot 2.3.2.RELEASE。
4. 配置
上次我们采用的是JavaConfig风格的配置,只需要向Spring IoC注入一个ConnectionFactory
。这一次我将尝试在application.yaml
中配置R2DBC的必要参数。
spring:
r2dbc:
url: r2dbcs:mysql://127.0.0.1:3306/r2dbc
username: root
password: 123456
以上就是R2DBC的主要配置。特别注意的是spring.r2dbc.url
的格式,根据数据库的不同写法是不同的,要看驱动的定义,这一点非常重要。连接池这里使用默认配置即可,不用显式定义。
5. 编写业务代码
接下来就是编写业务代码了。这里我还尝试使用DatabaseClient
来执行了DDL语句创建了client_user
表,感觉还不错。
@Autowired
DatabaseClient databaseClient;
@Test
void doDDL() {
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;"));
ddl.forEach(sql -> databaseClient.execute(sql)
.fetch()
.rowsUpdated()
.as(StepVerifier::create)
.expectNextCount(1)
.verifyComplete());
}
5.1 声明数据库实体
熟悉Spring Data JPA的同学应该很轻车熟路了。
/**
* the client user type
*
* @author felord.cn
*/
@Data
@Table
public class ClientUser implements Serializable {
private static final long serialVersionUID = -558043294043707772L;
@Id
private String userId;
private String nickName;
private String phoneNumber;
private Integer gender;
}
5.2 声明CRUD接口
上面实体类中的@Table
注解是有说法的,当我们的操作接口继承的是ReactiveCrudRepository<T, ID>
或者ReactiveSortingRepository<T, ID>
时,需要在实体类上使用@Table
注解,这也是推荐的用法。
public interface ReactiveClientUserSortingRepository extends ReactiveSortingRepository<ClientUser,String> {
}
当然实体类不使用@Table
注解标记时,我们还可以继承R2dbcRepository<T, ID>
接口。然后ReactiveClientUserSortingRepository
将提供一些操作数据库的方法。
然后Spring Data JPA怎么写,这里也差不多怎么写,但是有些功能现在还没有得到支持,比如上面提到的分页,还有主键策略等。
类似
PagingAndSortingRepository<T,ID>
的反应式分页功能接口目前还没有实装,会在未来的版本集成进来。
5.3 实际操作
接下来我们就要通过R2DBC实际操作MySQL数据库了。按照我们传统的逻辑写了如下的新增逻辑:
ClientUser clientUser = new ClientUser();
clientUser.setGender(2);
clientUser.setNickName("r2dbc");
clientUser.setPhoneNumber("9527");
clientUser.setUserId("snowflake");
Mono<ClientUser> save = reactiveClientUserSortingRepository.save(clientUser);
结果数据库并没有写入数据。这时因为r2dbc-mysql不能被直接使用,只能由客户端去实现并委托给客户端去操作。
这也是R2DBC的设计原则,R2DBC的目标是最小化SPI平面,目的是消除数据库之间的差异部分,并使得整个数据库完全具有反应式和背压。它主要用作客户端库使用的驱动程序SPI,而不打算直接在应用程序代码中使用。
所以这里我们可以借助于reactor-test
测试库去执行一下,改写为:
reactiveClientUserSortingRepository.save(clientUser)
.log()
.as(StepVerifier::create)
.expectNextCount(1)
.verifyComplete();
但是依然不能执行成功,提示update table [client_user]. Row with Id [snowflake] does not exist
,也就是说期望执行的是新增但是实际执行的是更新,由于数据库找不到主键为snowflake
的记录就报了错。这里为什么是更新呢?
这时因为实体类在进行新增时会判断主键是否填充,如果没有填充就认为是新数据,采取真正的新增操作,主键需要数据库来自动填充;如果主键存在值则认为是旧数据则调用更新操作。胖哥同Spring Data R2DBC的项目组沟通后并没有得到友好的解决方案,不过我已经找到了方法,这里先留个坑。
那么该如何新增一条数据呢?我们只能借助于@Query
注解来编写一条SQL
写入了:
@Modifying
@Query("insert into client_user (user_id,nick_name,phone_number,gender) values (:userId,:nickName,:phoneNumber,:gender)")
Mono<Integer> addClientUser(String userId, String nickName, String phoneNumber, Integer gender);
当添加了@Modifying
后,返回值可以从Mono<ClientUser>
、Mono<Boolean>
或者Mono<Integer>
任意一种选择。
reactiveClientUserSortingRepository
.addClientUser("snowflake",
"r2dbc",
"132****155",
0)
.as(StepVerifier::create)
.expectNextCount(1)
.verifyComplete();
这样就证明写成功了一条数据。
5.4 搭配Webflux使用
但是实际中该如何应用呢?目前能够想到的就是结合反应式框架Spring Webflux了,就像Spring Data JPA配合Spring MVC一样。
我们编写一个Webflux接口:
@RestController
@RequestMapping("/user")
public class ReactiveClientUserController {
@Autowired
private ReactiveClientUserSortingRepository reactiveClientUserSortingRepository;
/**
* 这里为了检验默认api 就不分层了
*
* @param userId the user id
* @return the mono
*/
@GetMapping("/{userId}")
public Mono<ClientUser> findUserById(@PathVariable String userId) {
return reactiveClientUserSortingRepository.findById(userId);
}
}
5.5 一些测试数据参考
在低并发时,Spring MVC + JDBC表现最佳,但在高并发下,WebFlux + R2DBC使用每个已处理请求的内存最少。
在高并发下,Spring MVC + JDBC的响应时间开始下降。显然,R2DBC在更高的并发性下提供了更好的响应时间。Spring WebFlux也比使用Spring MVC的类似实现更好。
6. 总结
今天对Spring Data R2DBC进一步演示,相信你能够从中学到一些东西。由于R2DBC还是比较新,还存在一些需要改进和补充的东西。目前社区非常活跃,发展十分迅速。好了今天的文章就到这里,原创不易多多关注:码农小胖哥 如果你觉得本文很有用,请点赞、转发、再看。
关注公众号:Felordcn 获取更多资讯
Spring Data R2DBC响应式操作MySQL的更多相关文章
- .NET 使用 MySql.Data.dll 动态库操作MySql的帮助类--MySqlHelper
.NET 使用 MySql.Data.dll 动态库操作MySql的帮助类--MySqlHelper 參考演示样例代码,例如以下所看到的: /// <summary> /// MySql ...
- 【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 ...
- 实战SpringCloud响应式微服务系列教程(第九章)使用Spring WebFlux构建响应式RESTful服务
本文为实战SpringCloud响应式微服务系列教程第九章,讲解使用Spring WebFlux构建响应式RESTful服务.建议没有之前基础的童鞋,先看之前的章节,章节目录放在文末. 从本节开始我们 ...
- 浅谈Spring 5的响应式编程
这篇使用Spring 5进行响应式编程的入门文章展示了你现在可以使用的一些新的non-blocking, asynchronous.感谢优锐课老师给予的指导! 近年来,由于响应式编程能够以声明性的方式 ...
- spring data jpa使用懒操作
如果model对象的某属性使用lazy load,调用这个属性时会报错, failed to lazily initialize a collection of role could not init ...
- 看Spring Data如何简化数据操作
Spring Data 概述 Spring Data 用于简化数据库访问,支持NoSQL 和 关系数据存储,其主要目标是使数据库的访问变得方便快捷. SpringData 项目所支持 NoSQL 存储 ...
- 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 ...
- php实现简单链式操作mysql数据库类
<?php $dbConfig = require_once(dirname(__FILE__).'/config.php'); class Db{ public $conn; ...
- spring data jpa的update操作
简介 使用jpa进行update操作主要有两种方式: 1.调用保存实体的方法 1)保存一个实体:repository.save(T entity) 2)保存多个实体:repository.save(I ...
随机推荐
- Redis系列(八):发布与订阅
Redis的发布与订阅,有点类似于消息队列,发送者往频道发送消息,频道的订阅者接收消息. 1. 发布与订阅示例 首先,在本机开启第1个Redis客户端,执行如下命令订阅blog.redis频道: SU ...
- Git篇--将代码上传到git完整版
1.注册github账号. 2.创建个人的github仓库,如图, 或者也可以进入个人中心去创建, 还可以直接点击右上角的“”+“”添加, 3.创建自己的Repository,如图: 4.新建 ...
- 获取本机SqlServer名称
using System.Data.Sql; //检索包含有关可用SQL Server实例的信息的表,必须先使用共享/静态Instance属性来检索枚举器 SqlDataSourceEnumerato ...
- day24 常用模块(下)
目录 一.logging模块 1 日志级别 2 默认级别为warning,默认打印到终端 3 为logging模块指定全局配置,针对所有的logger有效,控制打印到文件中 4.logging配置文件 ...
- MapReduce计算框架的核心编程思想
@ 目录 概念 MapReduce中常用的组件 概念 Job(作业) : 一个MapReduce程序称为一个Job. MRAppMaster(MR任务的主节点): 一个Job在运行时,会先启动一个进程 ...
- Navicat连接数据库报错2059 - authentication plugin...错误解决方法
今天使用Navicat 连接MySQL数据库出现错误:2059 - authentication plugin 'caching_sha2_password'. 出现这个错误的原因是因为MySQL8. ...
- 关于报错,Whoops! Lost connection to ws://XXX.XXX.XXX.XXX:15684/ws
昨天,在玩rabbitMQ时候,用stompJS从web连接ranbbitMQ时,报了标题的错误消息! 我把我这个html页面代码贴上,简单得讲,就是断开后,重新连接即可.
- 在运行vue项目时,执行npm install报错小记
在运行vue项目时,执行npm install 报错,导致后续的执行报各种错误,根据报错,尝试了网上的各种办法,最后发现时网络问题下载失败导致,解决办法: 安装cnpm==>npm instal ...
- Ethical Hacking - NETWORK PENETRATION TESTING(3)
Change MAC Address using macchanger. Packet Sniffing Basics Airodump-ng airodump-ng is a program pa ...
- 由一个计数器出发:关于vue使用独立js文件的问题
最近有个vue项目要用ztree. 然后,我想把一些逻辑提出来作为公共的方法,放到独立的js文件里. ztreeTool.js import $ from 'jquery' export defaul ...