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 R2DBCSpring Framework
1.0.0.RELEASE5.2.2.RELEASE
1.1.0.RELEASE5.2.6.RELEASE
1.1.1.RELEASE5.2.7.RELEASE
1.1.2.RELEASE5.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表,感觉还不错。

@AutowiredDatabaseClient databaseClient;@Testvoid 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@Tablepublic 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 获取更多资讯

集体博客:https://felord.cn