r2dbc介绍 及 动态数据源实现方式_wangqiso的博客-程序员秘密_r2dbc

技术标签: spring  java  数据库  

一、基本概念:

传统情况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

 

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/wangqiso/article/details/110821147

智能推荐

linux命令-cp命令_linux命令cp_ab_xue的博客-程序员秘密

cp命令简介 cp(Copy file):将源文件复制至目标文件 或将多个源文件复制至目标目录。语法 cp [选项] ... [-T] 源文件 目标文件 cp [选项] ... 源文件 目录 cp [选项] ... -t 目录 源文件选项-a, -archive : 等于-dR –preserve=all,与同时指定 -...

查找函数的加载地址及DynELF利用_Fasthand_的博客-程序员秘密

查找函数的加载地址指针寻找函数地址的典型方法是通过计算我们泄漏的同一个库中另一个函数的地址到所需函数的偏移量。但是,要使此方法有效工作,glibc的远程服务器版本需要与我们的版本相同。我们也可以通过泄漏一些函数并在libcdb.com中搜索来找到glibc的远程版本,但是有时这个方法会失败。DynELF如果我们有一个函数允许我们在任何给定的地址泄漏内存,我们可以使用类似DynELF使用pw...

H3C MSR 3600-28-X1路由器配置过程_h3cmsr3600配置教程_ZZYNDY的博客-程序员秘密

H3C MSR 3600-28-X1路由器配置硬件描述了解各模块功能LAN口WAN口光模块实验室现有安装情况硬件描述了解各模块功能LAN口WAN口光模块实验室现有安装情况

java中方法重载和方法重写分析_方法重载和重写分析题_lisansi9999的博客-程序员秘密

用简单的话来说方法重载(Overloading)就是在同一个类中,方法的名字相同,但参数个数、参数的类型或返回值类型不同,方法重写(Overriding)是指子类和父类的关系,子类重写了父类的方法,但方法名、参数类型、参数个数必须相同。      如果仅仅是需要粗浅的了解两者的区别上面的话相信已经能解决大部分人的问题了,但是做为进击的程序员我们当然不仅仅满足于此那么接下来我们着重分开来介绍一下

signature pad java_Ionic5手写签名SignaturePad_连玉君的博客-程序员秘密

初始化项目1. 首先新建一个Ionic5的项目:ionic start test-1 blank2. 安装对应的npm依赖:npm install angular2-signaturepad --save3. 依赖安装完成后在app.module.ts中注册该模块:// 模块路径import { SignaturePadModule } from 'angular2-signaturepad';@...

随便推点

[] taskService.completeTask(task.getId());的执行过程分析_iteye_3791的博客-程序员秘密

taskservice.completetask(task.getid());单步跟踪下去的顺序1、执行函数体内,得到dbid,感觉是注入到这个id去的。在taskimpl中有setdbid的方法2、commandservice.execute(new completetaskcmd(taskid));3、在completetaskcmd中,public void execute(...

Batch Size大小对训练过程的影响_guohahaya的博客-程序员秘密

(1)不考虑Batch Normalization的情况下,batch size的大小影响了深度学习训练过程中的完成每个epoch所需的时间和每次迭代(iteration)之间梯度的平滑程度。对于一个大小为N的训练集,如果每个epoch中mini-batch的采样方法采用最常规的N个样本每个都采样一次,设mini-batch大小为b,那么每个epoch所需的迭代次数(正向+反向)为,因此完...

python中关于__name__的作用_print(__name__)_突变的博客-程序员秘密

测试程序1,路径D:\Tensorflow\test\file\test.py当我们直接运行上面程序时,程序代码如下#testprint ('test name:',__name__)输出结果:可以看出此时__name__的值就等于__main__,表示当前程序为主程序直接运行测试程序2,当我们在上面程序test上级目录D:\Tensorflow\test下创建程序t

使用Nginx代理Docker的registry的HTTPS注意事项_nginx registry_世宝宝的博客-程序员秘密

最近服务器将80端口关闭了,在客户端使用Docker的时候出现了问题,官方文档是这样写的In the same train of thought, you must make sure you are properly sending the X-Forwarded-Proto, X-Forwarded-For, and Host headers to their “client-side” values. Failure to do so usually makes the registry iss

Windows 64-bit Oracle 12c 安装步骤_programer_33的博客-程序员秘密

1.下载安装包:这里需要自己注册一下,然后就可以登录下载软件了。下载地址:http://www.oracle.com/technetwork/database/enterprise-edition/downloads/index.html记得下载File1,File2 2.解压zip文件夹,值得注意的是,需要将两个压缩包解压到同一个目录下。两个压缩包中都有一个文件夹database

Oracle:Redhat 7.5+Oracle Rac 11.2.0.4 安装异常处理_Ryan_Bai的博客-程序员秘密

根据Oracle官方文档描述,Oracle 11g支持在Redhat 7上进行安装今天准备尝试进行安装,整体步骤与在Redhat 6上基本一致,配置方法详见:环境准备,此处不再进行赘述但在运行脚本时会遇到故障,具体情况如下一、异常描述二、原因分析因为RHEL 7使用systemd而不是initd运行进程和重启进程,而root.sh通过传统的initd运行ohasd进程。...

推荐文章

热门文章

相关标签