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

智能推荐

springmvc.xml文件中出现名称空间xmlns:context=“http://www.springframework.org/schema/context“报红的解决方法_springframework报红_码农研究僧的博客-程序员秘密

在idea编译器中在sprigmvc.xml的名称空间里出现http://www. springframework.org/schema/tx报红如图所示:第一个方法是让编译器自动查找可以通过此处的Fetch external resource可能好转也可能不为所动类似下面这种提示表示找不到那就需要手动进行添加该路径通过编译器的设置进行添加通过如下点击+号添加http://www. springframework.org/schema/tx之后显示就无异常...

一篇文章彻底掌握 HDFS 跨集群跨版本数据同步工具 hadoop disctp_hardoop distcp命令在原文件集群还是目标地址集群执行_明哥的IT随笔的博客-程序员秘密

大家好,我是明哥!最近有小伙伴问到 hadoop distcp 的使用,对其中的一些细节和容易踩的坑不是很清楚,所以今天我们来看下 hadoop distcp 的原理,细节和容易踩的坑。1...

如何使用postman上传文件_postman怎么导入文件_码农的进阶之路的博客-程序员秘密

进入postman,输入urlBody–&gt;选择form-data在框中输入controller要接收的字段,并选择上传类型,这里选择File点击Select File,弹出框 选择待上传的文件,然后点击Send即可发送该请求如果有其他字段,加上即可,如下...

升级Xcode之后VVDocumenter-Xcode不能用的解决办法-一般为意外点击了Skip Bundle导致..._aoduonve7966的博客-程序员秘密

  在苹果开发过程中,许多程序员都会使用到VVDocumenter-Xcode插件来进行注释操作,它是Xcode上一款可以快速添加标准注释,并可以自动生成文档的插件。有了VVDocumenter-Xcode,规范化的注释,只需要输入三个斜线“///”就可以搞定,非常方便实用,从Xcode 5开始,苹果要求加入UUID证书从而保证插件的稳定性。因此Xcode版本更新之后需要在VVDocum...

pyinstaller打包引用nmap包的软件时的错误处理_fifbro的博客-程序员秘密

使用pyinstaller打包python软件时出现了诸多问题,下面逐一对其记录。1. 针对打包后调用nmap等第三方软件会弹出控制台窗口的问题,修改nmap.py第228行部分的代码如下:IS_WIN32 = 'win32' in str(sys.platform).lower()if IS_WIN32: startupinfo = subprocess.STARTUPINFO() startupinfo.dwFlags = subprocess.CREATE_NEW_CONS

PureMVC 开发FlappyBird_唐孝辉的博客-程序员秘密

链接: https://pan.baidu.com/s/1DEeuIeuJ_0AtOuErshtcFA 提取码: byid

随便推点

基于Spring Cloud微服务化开发平台Cloud-Platform前端报错“网络错误!: http://localhost:8765/api/auth/captcha”_errors hide fetch errornot found http://localhost:_从今天起春暖花开的博客-程序员秘密

问题描述:mysql、nacos、redis、Cloud-Platform前后端均启动后,报错网络错误: http://localhost:8765/api/auth/captcha:原因分析:IDEA报错:Loading class `com.mysql.jdbc.Driver’. This is deprecated. The new driver is com.mysql.cj.jdbc.Driver。解决方案:修改ace-modules\ace-admin\src\main\resou

pyinstaller打包成无控制台可执行文件与popen冲突的解决办法_python spec 无控制台模式_Dawn_SSR的博客-程序员秘密

本文首发地址:https://www.dawnsite.cn/archives/200.html原因popen会打开一个管道执行命令,而管道是有输入(stdin)、输出(stdout)的,但当我们使用pyinstaller打包可执行文件时使用了-w参数或者是.spec文件中console=False,python解释器是不带控制台的,所以它没有办法处理输入(stdin)包括使用python的input()函数都不行os.popen实际上是一个简单的封装,原型是subprocess.popens.

Arduino101学习笔记(十一)—— 蓝牙BLE_KEVINZHAO124517的博客-程序员秘密

一、BLE技术简介 第四代蓝牙既包括传统的蓝牙,现在标有“蓝牙经典”,和新的低功耗蓝牙(Bluetooth LE,或BLE)。低数据速率,低功耗优化。 蓝牙LE广播就像一个社区公告栏。连接到它的计算机就像是阅读公告板的社区成员一样。每一个无线电作为一个公告板或读者。如果你的收音机是一个公告板(称为蓝牙LE的说法一个外围设备)揭示数据中的所有收音机在社区看。如果你的...

Java Socket通信实现多人多端网络画板聊天室_一旭日东升一的博客-程序员秘密

老规矩,先上实现的效果展示! Java Socket通信实现多人多端网络画板聊天室 本文介绍了一个基于Socket实现网络画板聊天室的完整过程,聊天室具备多人文本对话、同步绘图等功能。初尝试Socket简介Socket英文原意有插座、插孔的意思,在计算机术语中表示套接字。所谓套接字(Socket),就是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象。Socket就像一个邮

使用Android Studio制作NinePatch图片(9.png)_as nine patch 制作_NULL____的博客-程序员秘密

最近制作.9.png时发现,发现sdk的tools文件夹竟然没有draw9patch.bat了,原来Google把它集成到了Android Studio里面,使用起来更方便了。

linux下C++移动文件和文件夹 复制文件_nicholas_dfx的博客-程序员秘密

功能: 给一个文件重命名用 法: int rename(char *oldname, char *newname);头文件:#include &amp;lt;stdio.h&amp;gt;说明:用该函数可以实现文件移动功能,把一个文件的完整路径的盘符改一下就实现了这个文件的移动。具体参见下面的程序示例说明。#include &amp;lt;stdio.h&amp;gt;int rename(const char *oldpath...

推荐文章

热门文章

相关标签