在 Reactor 應用程式中使用 R2DBC

banq發表於2021-08-20

由於Reactor已經接管了 Java 世界,因此不可避免地會出現一個反應式 sql 庫。在這篇部落格中,我們將使用 r2dbc 和 h2 和 reactor。
可以在github上找到程式碼。
我們將從所需的依賴項開始。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
 
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.2</version>
    </parent>
 
    <groupId>com.gkatzioura</groupId>
    <artifactId>r2dbc-reactor</artifactId>
    <version>1.0-SNAPSHOT</version>
 
    <properties>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
    </properties>
 
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-r2dbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-commons</artifactId>
        </dependency>
 
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>io.r2dbc</groupId>
            <artifactId>r2dbc-h2</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>io.projectreactor</groupId>
            <artifactId>reactor-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
 
</project>

我們從 r2dbc、h2 r2dbc 驅動程式、h2 二進位制檔案以及測試工具匯入 spring 資料。
 postgresql表結構:

create table order_request (
    id uuid NOT NULL constraint or_id_pk primary key,
    created_by varchar,
    created timestamp default now()              not null,
    updated timestamp default now()              not null
);

我們稍後會將其新增到 test/resources/schema.sql 以進行測試。
另外讓我們新增一個新模型:

package com.gkatzioura.r2dbc.model;
 
import java.time.LocalDateTime;
import java.util.UUID;
 
import org.springframework.data.annotation.Id;
import org.springframework.data.domain.Persistable;
import org.springframework.data.relational.core.mapping.Table;
 
@Table("order_request")
public class OrderRequest implements Persistable<UUID> {
 
    @Id
    private UUID id;
    private String createdBy;
    private LocalDateTime created;
    private LocalDateTime updated;
 
    public void setId(UUID id) {
        this.id = id;
    }
 
    public String getCreatedBy() {
        return createdBy;
    }
 
    public void setCreatedBy(String createdBy) {
        this.createdBy = createdBy;
    }
 
    public LocalDateTime getCreated() {
        return created;
    }
 
    public void setCreated(LocalDateTime created) {
        this.created = created;
    }
 
    public LocalDateTime getUpdated() {
        return updated;
    }
 
    public void setUpdated(LocalDateTime updated) {
        this.updated = updated;
    }
 
    @Override
    public UUID getId() {
        return id;
    }
 
    @Override
    public boolean isNew() {
        return created == null;
    }
 
}


注意isNew方法。透過這種方式,儲存庫可以確定物件是否應該被持久化或更新。
現在開始我們的儲存庫

package com.gkatzioura.r2dbc.repository;
 
import java.util.UUID;
import org.springframework.data.repository.reactive.ReactiveCrudRepository;
import com.gkatzioura.r2dbc.model.OrderRequest;
 
public interface OrderRepository extends ReactiveCrudRepository<OrderRequest, UUID> {
}


進行一些測試。
如上所述,上面的模式將駐留在 test/resources/schema.sql 中
我們將為測試 h2 db 新增一些配置。我們需要確保 h2 會選擇 postgresql 介面。

package com.gkatzioura.r2dbc;
 
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.r2dbc.config.AbstractR2dbcConfiguration;
import org.springframework.data.r2dbc.repository.config.EnableR2dbcRepositories;
import org.springframework.r2dbc.connection.init.CompositeDatabasePopulator;
import org.springframework.r2dbc.connection.init.ConnectionFactoryInitializer;
import org.springframework.r2dbc.connection.init.ResourceDatabasePopulator;
 
import io.r2dbc.h2.H2ConnectionFactory;
import io.r2dbc.spi.ConnectionFactory;
 
@Configuration
@EnableR2dbcRepositories
public class H2ConnectionConfiguration extends AbstractR2dbcConfiguration  {
 
    @Override
    public ConnectionFactory connectionFactory() {
        return new H2ConnectionFactory(
                io.r2dbc.h2.H2ConnectionConfiguration.builder()
                                                     .url("mem:testdb;MODE=PostgreSQL;DB_CLOSE_DELAY=-1;")
                                                     .build()
        );
    }
 
    @Bean
    public ConnectionFactoryInitializer initializer() {
        var initializer = new ConnectionFactoryInitializer();
        initializer.setConnectionFactory(connectionFactory());
 
        var databasePopulator = new CompositeDatabasePopulator();
        databasePopulator.addPopulators(new ResourceDatabasePopulator(new ClassPathResource("schema.sql")));
        initializer.setDatabasePopulator(databasePopulator);
        return initializer;
    }
 
}



透過此配置,我們建立了一個模擬 Postgresql 資料庫的 H2 資料庫,我們建立了模式並啟用了 R2DBC 儲存庫的建立。
另外讓我們新增一個測試。

package com.gkatzioura.r2dbc.repository;
 
import java.util.UUID;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import com.gkatzioura.r2dbc.H2ConnectionConfiguration;
import com.gkatzioura.r2dbc.model.OrderRequest;
import reactor.test.StepVerifier;
 
@ExtendWith({SpringExtension.class})
@Import({H2ConnectionConfiguration.class})
class OrderRepositoryTest {
 
    @Autowired
    private OrderRepository orderRepository;
 
    @Test
    void testSave() {
        UUID id = UUID.randomUUID();
        OrderRequest orderRequest = new OrderRequest();
        orderRequest.setId(id);
        orderRequest.setCreatedBy("test-user");
 
        var persisted = orderRepository.save(orderRequest)
                                       .map(a -> orderRepository.findById(a.getId()))
                                       .flatMap(a -> a.map(b -> b.getId()));
 
        StepVerifier.create(persisted).expectNext(id).verifyComplete();
    }
}

 

相關文章