Reactive Spring实战 -- 响应式MySql交互
本文与大家探讨Spring中如何实现MySql响应式交互。
Spring Data R2DBC项目是Spring提供的数据库响应式编程框架。
R2DBC是Reactive Relational Database Connectivity的首字母缩写词。 R2DBC是一个API规范倡议,它声明了一个响应式API,由驱动程序供应商实现,并以响应式编程的方式访问他们的关系数据库。
实现数据库的响应式编程并不是容易的,传统的JDBC协议是一个完全阻塞的 API,所以响应式编程对JDBC协议可以说是一种“颠覆”了。
这里再强调一次响应式编程,响应式编程是一种非阻塞异步的编程模式,而Spring响应式编程提供了一种友好、直观、易于理解的编码模式处理异步结果(可参考前面的文章)。
也就是说,应用发送SQL给数据库后,应用线程不需要阻塞等待数据库返回结果,而是直接返回处理其他任务,等到数据库SQL处理完成后,再由Spring调用线程处理结果。
到目前,Spring Data R2DBC项目支持以下数据库:
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)
下面基于MySql,介绍一下Spring Data R2DBC使用方式。
引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-r2dbc</artifactId>
</dependency>
<dependency>
<groupId>dev.miku</groupId>
<artifactId>r2dbc-mysql</artifactId>
<version>0.8.2.RELEASE</version>
</dependency>
配置文件
spring.r2dbc.url=r2dbcs:mysql://127.0.0.1:3306/bin-springreactive?useSSL=false
spring.r2dbc.username=...
spring.r2dbc.password=...
Spring Data R2DBC可以与Spring Data JPA结合使用,其实R2DBC与原来的JPA使用方式差别不大,使用非常简单。
只是Spring Data JPA中方法返回的是真实的值,而R2DBC中,返回的是数据流Mono,Flux。
简单介绍一个Spring Data JPA。Spring Data JPA是Spring基于ORM框架、JPA规范的基础上封装的一套 JPA (Java Persistence API) 应用框架,简单说,就是类似Mybatis,Hibernate的框架(Spring Data JPA底层通过Hibernate操作数据库)。
Repository是Spring Data R2DBC中的重要概念,封装了对一个实体的操作,相当于一个dao(Data Access Object,数据访问对象)。
假如应用中有一个实体DeliveryCompany,对应表delivery_company。
实体定义如下:
public class DeliveryCompany {
@Id
private long id;
private String name;
private String label;
private Integer level;
...
}
@Id
注解标志了id属性。
下面我们定义一个DeliveryCompanyRepository接口,继承与R2dbcRepository。
@Repository
public interface DeliveryCompanyRepository extends R2dbcRepository<DeliveryCompany,Long> {
...
}
R2dbcRepository是Spring实现的接口,该接口继承与ReactiveCrudRepository,ReactiveCrudRepository接口提供了增删改查的模板方法。
public interface ReactiveCrudRepository<T, ID> extends Repository<T, ID> {
<S extends T> Mono<S> save(S var1);
<S extends T> Flux<S> saveAll(Iterable<S> var1);
<S extends T> Flux<S> saveAll(Publisher<S> var1);
Mono<T> findById(ID var1);
Mono<T> findById(Publisher<ID> var1);
...
}
注意这里的返回结果,是Mono、Flux等异步结果,这就是响应式交互与非响应式交互的最大区别。
如果要自定义操作,有以下方式
(1) 通过方法名定义
只要我们按规则定义方法名,Spring就会为我们生成SQL。
// 按名称查找
Flux<DeliveryCompany> findByName(String name);
// 查找给定范围内的
Flux<DeliveryCompany> findByIdGreaterThan(Long startId);
// 查找大于给定id的数据
Flux<DeliveryCompany> findByIdGreaterThan(Long startId);
// 查询名称以给定字符串开头的数据
Flux<DeliveryCompany> findByNameStartingWith(String start);
// 分页
Flux<DeliveryCompany> findByIdGreaterThanEqual(Long startId, Pageable pageable);
注意,上面方法名需要按规范定义
findByName -> findBy<fieldName>
findByIdGreaterThan -> findBy<fieldName>GreaterThan
Spring会为我们生成对应的SQL,非常方便。这种方法可以满足多数简单的查询。
对应的还有删除操作
Mono<Integer> deleteByName(String name);
详细的方法命名规则,则参考官方文档。
(2)手动编写SQL
对于复杂的SQL,开发人员也可以手写SQL,
@Query("select id,name from delivery_company where id in (:ids)")
Flux<DeliveryCompany> findByIds2(List<Long> ids);
@Query("select id,name from delivery_company where name = :name")
Flux<DeliveryCompany> findByName2(String name);
@Modifying
@Query("update delivery_company set name = :name where id = :id")
Mono<DeliveryCompany> update2(@Param("id") long id, @Param("name") String name);
可以看到,编写SQL也非常简单,对于集合参数支持非常好。
目前未发现使用JPQL(Java Persistence Query Language)的方式,不过使用原生的SQL是没有问题的。
如果大家使用过Mybatis,应该会用过以下判断参数非空的做法
<select id="findByName2"
resultType="DeliveryCompany">
SELECT * FROM delivery_company
WHERE name = #{name}
<if test="label != null">
AND label like #{label}
</if>
</select>
可惜在JPA中非找到支持的方法,如果有同学知道,请不吝指教。
(3) 使用R2dbcEntityTemplate
另外,可以使用R2dbcEntityTemplate自动生成SQL
@Autowired
private R2dbcEntityTemplate template;
public Flux<DeliveryCompany> getByName3(String name) {
return template
.select(DeliveryCompany.class)
.from("delivery_company")
.matching(Query.query(Criteria.where("name").is(name))).all();
// Criteria.where("name").is(name).and
}
public Mono<Integer> update3(DeliveryCompany company) {
return template
.update(DeliveryCompany.class)
.inTable("delivery_company")
.matching(Query.query(Criteria.where("id").is(company.getId())))
.apply(Update.update("name", company.getName()));
}
这种方式可以实现判断参数非空查询,不过使用起来较为繁琐(我们也可以对其进行一定的封装以方便我们使用)。
(4)Spring Data R2DBC中同样支持Querydsl,
我们定义的Repository可以继承于ReactiveQuerydslPredicateExecutor,该接口提供以下模板方法
public interface ReactiveQuerydslPredicateExecutor<T> {
Mono<T> findOne(Predicate var1);
Flux<T> findAll(Predicate var1);
Flux<T> findAll(Predicate var1, Sort var2);
Flux<T> findAll(Predicate var1, OrderSpecifier... var2);
Flux<T> findAll(OrderSpecifier... var1);
Mono<Long> count(Predicate var1);
Mono<Boolean> exists(Predicate var1);
}
Spring Data R2DBC中同样支持@QuerydslPredicate注解,这里不再深入。
Spring Data R2DBC支持事务,使用方法很简单,在业务方法添加@Transactional即可
@Transactional
public Flux<DeliveryCompany> save(List<DeliveryCompany> companyList) {
Flux<DeliveryCompany> result = Flux.just();
for (DeliveryCompany deliveryCompany : companyList) {
result = result.concat(result, repository.save(deliveryCompany));
}
return result;
}
为了展示事务的使用,这里没有调用Repository的saveAll方法,而是循环插入数据并返回最后的结果。
注意,最后的结果Flux、Mono一定要作为方法返回值,因为响应式编程的异常信息保存在这些结果中(而不是在方法调用时抛出),所以这些结果必须作为方法返回值,否则Spring无法知道方法是否报错,也就无法回退事务。
Spring Data R2DBC基本与Spring Data JPA的使用相同,所以本篇文章主要还是对Spring Data JPA使用方式的介绍。
我之前并没有使用过Spring Data JPA,本篇文章主要还是入门介绍,还有很多东西没有涉及,如id生成,多表查询等,这里不再一一介绍。
官方文档:https://docs.spring.io/spring-data/r2dbc/docs/1.3.2/reference/html/
文章完整代码:https://gitee.com/binecy/bin-springreactive/tree/master/delivery-service
如果您觉得本文不错,欢迎关注我的微信公众号,系列文章持续更新中。您的关注是我坚持的动力!
Reactive Spring实战 -- 响应式MySql交互的更多相关文章
- Reactive Spring实战 -- 响应式Redis交互
本文分享Spring中如何实现Redis响应式交互模式. 本文将模拟一个用户服务,并使用Redis作为数据存储服务器. 本文涉及两个java bean,用户与权益 public class User ...
- Reactive Spring实战 -- 响应式Kafka交互
本文分享如何使用KRaft部署Kafka集群,以及Spring中如何实现Kafka响应式交互. KRaft 我们知道,Kafka使用Zookeeper负责为kafka存储broker,Consumer ...
- Reactive 理解 SpringBoot 响应式的核心-Reactor
Reactive 理解 SpringBoot 响应式的核心-Reactor bestcoding 2020-02-23 17:26:43 一.前言 关于 响应式 Reactive,前面的两篇文章谈了不 ...
- 第二百五十一节,Bootstrap项目实战--响应式轮播图
Bootstrap项目实战--响应式轮播图 学习要点: 1.响应式轮播图 本节课我们要在导航条的下方做一张轮播图,自动播放最新的重要动态. 一.响应式轮播图 响应式轮播图 第一步,设置轮播器区域car ...
- 第二百五十节,Bootstrap项目实战--响应式导航
Bootstrap项目实战--响应式导航 学习要点: 1.响应式导航 一.响应式导航 基本导航组件+响应式 第一步,声明导航区域,设置导航默认样式,设置导航条固定在顶部navbar样式class类,写 ...
- Java9第四篇-Reactive Stream API响应式编程
我计划在后续的一段时间内,写一系列关于java 9的文章,虽然java 9 不像Java 8或者Java 11那样的核心java版本,但是还是有很多的特性值得关注.期待您能关注我,我将把java 9 ...
- Spring 5 响应式编程
要点 Reactor 是一个运行在 Java8 之上的响应式流框架,它提供了一组响应式风格的 API 除了个别 API 上的区别,它的原理跟 RxJava 很相似 它是第四代响应式框架,支持操作融合, ...
- Reactive(1) 从响应式编程到"好莱坞"
目录 概念 面向流设计 异步化 响应式宣言 参考文档 概念 Reactive Programming(响应式编程)已经不是一个新东西了. 关于 Reactive 其实是一个泛化的概念,由于很抽象,一些 ...
- Reactive Spring实战 -- WebFlux使用教程
WebFlux是Spring 5提供的响应式Web应用框架. 它是完全非阻塞的,可以在Netty,Undertow和Servlet 3.1+等非阻塞服务器上运行. 本文主要介绍WebFlux的使用. ...
随机推荐
- Jenkins——安装部署
1.部署Jdk 由于jenkins需要jdk环境,所以先部署jdk,解压并设置环境变量就行: # tar zxf jdk-8u45-linux-x64.tar.gz # mv jdk-8u45-lin ...
- 1.1Ubuntu安装
在虚拟机中安装 Ubuntu 步骤 安装前的准备和基本安装 设置语言环境 安装常用软件 1. 安装前的准备和基本安装 1.1 安装前的准备 访问 http://cn.ubuntu.com/downlo ...
- strcpy和memcpy的区别-(转自stone Jin)
strcpy和memcpy都是标准C库函数,它们有下面的特点.strcpy提供了字符串的复制.即strcpy只用于字符串复制,并且它不仅复制字符串内容之外,还会复制字符串的结束符. 已知strcpy函 ...
- Centos7 LVM管理的逻辑卷根目录扩容和/var目录扩容
Centos7 LVM管理的逻辑卷根目录扩容 fdisk /dev/sdb #对新加磁盘进行分区操作pvcreate /dev/sdb1 #创建一个物理卷vgs #查看现有的卷组vgextend ce ...
- 【JVM进阶之路】十三:类加载过程
通过前面的学习,我们了解了Class文件的结构,在Class文件中描述的各类信息,最终都需要加载到虚拟机中之后才能被运行和使用. 接下来,我们开始学习JVM的类加载. 一个类从被加载到虚拟机内存中开始 ...
- Log4j实战,依赖分析
背景 在项目中经常被log4j的各种依赖冲突搞的焦头烂额,久病成良医啊,在这里记录一下我对log4j的理解与分析 log4j 与 log4j2 log4j2是log4j的升级版,二者互不兼容,据说lo ...
- CVPR2019:无人驾驶3D目标检测论文点评
CVPR2019:无人驾驶3D目标检测论文点评 重读CVPR2019的文章,现在对以下文章进行点评. Stereo R-CNN based 3D Object Detection for Autono ...
- TVM在ARM GPU上优化移动深度学习
TVM在ARM GPU上优化移动深度学习 随着深度学习的巨大成功,将深度神经网络部署到移动设备的需求正在迅速增长.与在台式机平台上所做的类似,在移动设备中使用GPU可以提高推理速度和能源效率.但是,大 ...
- .NET平台系列14 .NET5中的新增功能
系列目录 [已更新最新开发文章,点击查看详细] .NET5中不包含的内容 尽管 .NET5 框架中提供了一组重要 API,但它并不包括过去20年左右开发的所有 API,但是.NET Stand ...
- jQuery基础-选择器,样式操作
入口函数:ready() 当 DOM(文档对象模型) 已经加载,并且页面(包括图像)已经完全呈现时,会发生 ready 事件. 由于该事件在文档就绪后发生,因此把所有其他的 jQuery 事件和函数置 ...