1. 程式人生 > 其它 >r2dbc介紹 及 動態資料來源實現方式

r2dbc介紹 及 動態資料來源實現方式

技術標籤:資料庫javaspring

一、基本概念:

傳統情況Java 使用 JDBC 來操作關係型資料庫,而 JDBC 是阻塞的、同步的,即使使用執行緒池進行改善也是有限的。基於此,Spring官方(Pivotal)提出了R2DBC(Reactive Relational Database Connectivity)。R2DBC是一項API規範計劃,它聲明瞭一個反應式API,該方法將由資料庫廠商實現以訪問其關係資料庫。

目前實現了R2DBC的資料庫驅動有:H2、MariaDB、Microsoft SQL Server、MySQLjasync-sql MySQL、Postgres

使用maven新增兩個依賴:r2dbc 介面、資料庫驅動實現。

例如:

        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-r2dbc</artifactId>
            <version>1.2.1</version>
        </dependency>
        <dependency>
            <groupId>com.github.jasync-sql</groupId>
            <artifactId>jasync-r2dbc-mysql</artifactId>
            <version>1.1.4</version>
        </dependency>

注:Maven Central到目前為止還沒有R2DBC工件,因此我們還需要在專案中新增幾個Spring的儲存庫。

<repositories>
    <repository>
        <id>spring-milestones</id>
        <name>Spring Milestones</name>
        <url>https://repo.spring.io/milestone</url>
        <snapshots>
            <enabled>false</enabled>
        </snapshots>
   </repository>
   <repository>
       <id>spring-snapshots</id>
       <name>Spring Snapshots</name>
       <url>https://repo.spring.io/snapshot</url>
       <snapshots>
           <enabled>true</enabled>
       </snapshots>
    </repository>
</repositories>

二、直接使用r2bdc

使用r2dbc訪問關係型資料庫的核心是建立一個io.r2dbc.spi.ConnectionFactory介面的例項(通常使用單例)。如:

import io.r2dbc.spi.ConnectionFactories;
import io.r2dbc.spi.ConnectionFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import reactor.test.StepVerifier;

import org.springframework.data.r2dbc.core.R2dbcEntityTemplate;

public class R2dbcApp {

  private static final Log log = LogFactory.getLog(R2dbcApp.class);

  public static void main(String[] args) {

    ConnectionFactory connectionFactory = ConnectionFactories.get("r2dbc:h2:mem:///test?options=DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE");

    R2dbcEntityTemplate template = new R2dbcEntityTemplate(connectionFactory);

    template.getDatabaseClient().sql("CREATE TABLE person" +
        "(id VARCHAR(255) PRIMARY KEY," +
        "name VARCHAR(255)," +
        "age INT)")
      .fetch()
      .rowsUpdated()
      .as(StepVerifier::create)
      .expectNextCount(1)
      .verifyComplete();

    template.insert(Person.class)
      .using(new Person("joe", "Joe", 34))
      .as(StepVerifier::create)
      .expectNextCount(1)
      .verifyComplete();

    template.select(Person.class)
      .first()
      .doOnNext(it -> log.info(it))
      .as(StepVerifier::create)
      .expectNextCount(1)
      .verifyComplete();
  }
}

也可通過spring的方式建立連線工廠。

@Configuration
public class ApplicationConfiguration extends AbstractR2dbcConfiguration {

  @Override
  @Bean
  public ConnectionFactory connectionFactory() {
    return …
  }
}

ConnectionFactory建立完成後,可通過它得到訪問資料庫的操作類:R2dbcEntityTemplate(提供了面向實體的資料庫訪問操作)。

三、使用spring repository 支援

需要使用註解@EnableR2dbcRepositories開啟功能。如下

@Configuration
@EnableR2dbcRepositories
class ApplicationConfig extends AbstractR2dbcConfiguration {

  @Override
  public ConnectionFactory connectionFactory() {
    return …
  }
}

再建立相應的repository介面

public interface PersonRepository extends ReactiveCrudRepository<Person, Long> {

  // additional custom query methods go here
}

然後就可以使用repository特性來進行資料庫操作了,例如:

@ExtendWith(SpringExtension.class)
@ContextConfiguration
class PersonRepositoryTests {

  @Autowired
  PersonRepository repository;

  @Test
  void readsAllEntitiesCorrectly() {

    repository.findAll()
      .as(StepVerifier::create)
      .expectNextCount(1)
      .verifyComplete();
  }

  @Test
  void readsEntitiesByNameCorrectly() {

    repository.findByFirstname("Hello World")
      .as(StepVerifier::create)
      .expectNextCount(1)
      .verifyComplete();
  }
}

四、r2dbc動態資料來源的實現方案

r2dbc提供了org.springframework.r2dbc.connection.lookup.AbstractRoutingConnectionFactory抽象類,並需要我們自己實現protected abstract Mono<Object> determineCurrentLookupKey();方法以達到動態切換資料來源的目的。

專案中實現動態資料來源的流程如下:

1、spring ioc啟動時,通過jdbc方式載入中心庫資料來源,由中心庫查詢對話庫配置資訊及相應公司id與serverKey對應關係(serverKey與資料來源一對一,公司id與serverKey多對一)

2、通過對話庫配置資訊建立多個ConnectionFactory,得到 Map<String, ConnectionFactory> connectionFactoryMap。此hash的key為serverKey,value為一個可用的ConnectionFactory

3、呼叫AbstractRoutingConnectionFactorypublic void setTargetConnectionFactories(Map<?, ?> targetConnectionFactories)方法設定值連線工廠集合

4、呼叫AbstractRoutingConnectionFactorypublic void setDefaultTargetConnectionFactory(Object defaultTargetConnectionFactory)設定預設連線工廠

5、重寫protected abstract Mono<Object> determineCurrentLookupKey()方法。從**Context**中獲取公司id對應的**serverKey**

注:此處需要使用到reactor的高階特性:Context(具有與ThreadLocal類似的功能),它是一個鍵值對的資料結構。它作用於一個 Flux或一個 Mono上,而不是應用於一個執行緒(Thread)。

並且它使用 Subscription的傳播機制來讓自己對每一個操作符都可見(從最後一個 subscribe沿鏈向上)。因此需要在呼叫鏈最後或儘可能後的位置 (呼叫 subscribe()前 )建立Context並將其繫結到FluxMono上。

6、將我們自己實現的AbstractRoutingConnectionFactory的子類通過@Bean注入ioc容器

7、在進行資料庫操作時將公司id繫結到FluxMono中的Context上。

參考文件:

https://docs.spring.io/spring-data/r2dbc/docs/current/reference/html/#get-started:first-steps:what

https://projectreactor.io/docs/core/release/reference/index.html#context