前沿技術(shù)探索:Spring Data R2DBC響應(yīng)式操作MySQL

1. 前言

使用R2DBC操作MySQL數(shù)據(jù)庫 一文中初步介紹了r2dbc-mysql的使用。由于借助DatabaseClient操作MySQL,過于初級和底層,不利于開發(fā)。今天就利用Spring Data R2DBC來演示Spring 數(shù)據(jù)存儲抽象(Spring Data Repository)風格的R2DBC數(shù)據(jù)庫操作。

請注意:目前Spring Data R2DBC雖然已經(jīng)迭代了多個正式版,但是仍然處于初級階段,還不足以運用到生產(chǎn)中。不過未來可期,值得研究學習。

2. Spring Data R2DBC

Spring Data R2DBC提供了基于R2DBC反應(yīng)式關(guān)系數(shù)據(jù)庫驅(qū)動程序的流行的Repository抽象。但是這并不是一個ORM框架,你可以把它看做一個數(shù)據(jù)庫訪問的抽象層或者R2DBC的客戶端程序。它不提供ORM框架具有的緩存、懶加載等諸多特性,但它抽象了數(shù)據(jù)庫和對象的抽象映射關(guān)系,具有輕量級、易用性的特點。

2.1 版本對應(yīng)關(guān)系

胖哥總結(jié)了截至目前Spring Data R2DBCSpring Framework的版本對應(yīng)關(guān)系:

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

一定要注意版本對應(yīng)關(guān)系,避免不兼容的情況。

3. 基礎(chǔ)依賴

上次我沒有引用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>
<!--自動配置需要引入的一個嵌入式數(shù)據(jù)庫類型對象-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<!-- 反應(yīng)式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的必要參數(shù)。

spring:
  r2dbc:
    url: r2dbcs:mysql://127.0.0.1:3306/r2dbc
    username: root
    password: 123456

以上就是R2DBC的主要配置。特別注意的是spring.r2dbc.url的格式,根據(jù)數(shù)據(jù)庫的不同寫法是不同的,要看驅(qū)動的定義,這一點非常重要。連接池這里使用默認配置即可,不用顯式定義。

5. 編寫業(yè)務(wù)代碼

接下來就是編寫業(yè)務(wù)代碼了。這里我還嘗試使用DatabaseClient來執(zhí)行了DDL語句創(chuàng)建了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 聲明數(shù)據(jù)庫實體

熟悉Spring Data JPA的同學應(yīng)該很輕車熟路了。

/**
 *  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將提供一些操作數(shù)據(jù)庫的方法。

然后Spring Data JPA怎么寫,這里也差不多怎么寫,但是有些功能現(xiàn)在還沒有得到支持,比如上面提到的分頁,還有主鍵策略等。

類似PagingAndSortingRepository<T,ID>的反應(yīng)式分頁功能接口目前還沒有實裝,會在未來的版本集成進來。

5.3 實際操作

接下來我們就要通過R2DBC實際操作MySQL數(shù)據(jù)庫了。按照我們傳統(tǒng)的邏輯寫了如下的新增邏輯:

ClientUser clientUser = new ClientUser();

clientUser.setGender(2);
clientUser.setNickName("r2dbc");
clientUser.setPhoneNumber("9527");
clientUser.setUserId("snowflake");

Mono<ClientUser> save = reactiveClientUserSortingRepository.save(clientUser);

結(jié)果數(shù)據(jù)庫并沒有寫入數(shù)據(jù)。這時因為r2dbc-mysql不能被直接使用,只能由客戶端去實現(xiàn)并委托給客戶端去操作。

這也是R2DBC的設(shè)計原則,R2DBC的目標是最小化SPI平面,目的是消除數(shù)據(jù)庫之間的差異部分,并使得整個數(shù)據(jù)庫完全具有反應(yīng)式和背壓。它主要用作客戶端庫使用的驅(qū)動程序SPI,而不打算直接在應(yīng)用程序代碼中使用。

所以這里我們可以借助于reactor-test測試庫去執(zhí)行一下,改寫為:

reactiveClientUserSortingRepository.save(clientUser)
        .log()
        .as(StepVerifier::create)
        .expectNextCount(1)
        .verifyComplete();

但是依然不能執(zhí)行成功,提示update table [client_user]. Row with Id [snowflake] does not exist ,也就是說期望執(zhí)行的是新增但是實際執(zhí)行的是更新,由于數(shù)據(jù)庫找不到主鍵為snowflake的記錄就報了錯。這里為什么是更新呢?

這時因為實體類在進行新增時會判斷主鍵是否填充,如果沒有填充就認為是新數(shù)據(jù),采取真正的新增操作,主鍵需要數(shù)據(jù)庫來自動填充;如果主鍵存在值則認為是舊數(shù)據(jù)則調(diào)用更新操作。胖哥同Spring Data R2DBC的項目組溝通后并沒有得到友好的解決方案,不過我已經(jīng)找到了方法,這里先留個坑。

那么該如何新增一條數(shù)據(jù)呢?我們只能借助于@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();

[圖片上傳失敗...(image-ef59a-1595988552618)]

<figcaption></figcaption>

這樣就證明寫成功了一條數(shù)據(jù)。

5.4 搭配Webflux使用

但是實際中該如何應(yīng)用呢?目前能夠想到的就是結(jié)合反應(yīng)式框架Spring Webflux了,就像Spring Data JPA配合Spring MVC一樣。

我們編寫一個Webflux接口:

@RestController
@RequestMapping("/user")
public class ReactiveClientUserController {

    @Autowired
    private ReactiveClientUserSortingRepository reactiveClientUserSortingRepository;

    /**
     * 這里為了檢驗?zāi)Japi 就不分層了
     *
     * @param userId the user id
     * @return the mono
     */
    @GetMapping("/{userId}")
    public Mono<ClientUser> findUserById(@PathVariable String userId) {
        return reactiveClientUserSortingRepository.findById(userId);
    }

}

[圖片上傳失敗...(image-b03bb1-1595988552618)]

<figcaption></figcaption>

5.5 一些測試數(shù)據(jù)參考

在低并發(fā)時,Spring MVC + JDBC表現(xiàn)最佳,但在高并發(fā)下,WebFlux + R2DBC使用每個已處理請求的資源占用最少。

在高并發(fā)下,Spring MVC + JDBC的響應(yīng)時間開始下降。顯然,R2DBC在更高的并發(fā)性下提供了更好的響應(yīng)時間。Spring WebFlux也比使用Spring MVC的類似實現(xiàn)更好。

6. 總結(jié)

今天對Spring Data R2DBC進一步演示,相信你能夠從中學到一些東西。由于R2DBC還是比較新,還存在一些需要改進和補充的東西。目前社區(qū)非?;钴S,發(fā)展十分迅速。

作者:碼農(nóng)小胖哥
鏈接:https://juejin.im/post/5f20caad6fb9a07e6f7b915b
來源:掘金

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容