写点什么

Reactive Spring 实战 -- 响应式 MySql 交互

用户头像
binecy
关注
发布于: 2 小时前

本文与大家探讨 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=falsespring.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。

@Repositorypublic 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


如果您觉得本文不错,欢迎关注我的微信公众号,系列文章持续更新中。您的关注是我坚持的动力!


发布于: 2 小时前阅读数: 4
用户头像

binecy

关注

还未添加个人签名 2020.08.26 加入

还未添加个人简介

评论

发布
暂无评论
Reactive Spring实战 -- 响应式MySql交互