如何编程实现从多数据库操作数据

news2024/11/13 9:12:23

对于数据量很大的复杂系统,有时候会采用分库或者分表的减轻单台数据库服务器压力,截止目前有一些工具直接支持读写分离等,例如ShardingSphere,如果不采用工具框架,从编码出发,如何实现从多个数据库读写数据呢?本篇博客将介绍三种代码实现思路,并提供完整Demo。

方式一:将不同DB的操作存放到不同目录下来实现分库操作

通过该方式达到分库的操作,实际很简单,第一:在application.properties文件中通过不同的前缀区分不同的数据库,第二:为不同的database编写配置类,在配置类中定义了repository的目录,通过前缀创建不同的Database bean,第三,在不同的目录编写service,repository,controller.这样在不同controller调用service,service再调用repository时,因为不同的数据库配置的repository目录不同,故调用不同controller时,实际使用的是不同的数据库。这种实现思路,本质实现的目标是将不同的表放入不同Database中,并不是将相同的表放入不同的数据库,例如读写分离。完整代码可查看Demo1。

方式二:通过切面编程实现读写分离

实际业务场景中,为了提升数据库处理数据能力,可能需要进行读写分离,即同一张表,写操作在一个数据库,读操作在另外一个数据库,这两个数据库存放了相同的表数据。那如何编写代码实现这一目标呢?如下图所示,首先在application.properties文件中,通过不同的前缀来区分数据库,在配置文件类中定义不同DataSource bean,接着在切面代码中,如果方法是以find,select,query,search等开头,那么去查询slave数据库,否则从master数据库操作数据,这样就实现了读写分离。

上面代码片段中本质是通过DataSourceContextHolder来切换数据库的?而DataSourceContextHolder本身只有一些get(),set()方法,背后是如何工作的呢?如下图代码所示,在定义EntityManagerFactoryBean的时候注入了routingDataSource,而RoutingDataSource默认会设置masterDataSource为默认的目标数据库,当通过DataSourceContextHolder重新设置目标数据库后,RoutingDataSource中的目标数据库会同步更新,而EntityManagerFactoryBean是通过routingDataSource构建出来的,所以,最终实现了不同数据库的切换操作。完整代码可查看Demo2。

方式三:通过切面编程实现分表

如下图所示,虽然都是通过切面编程的方式,但是这个切面是针对注解,当添加注解@SwithDataSource(value="xxdb")时,下面的repository都切换到了对应的数据库,右边图片是切面编程的部分代码,本质是调用AbstractRoutingDataSourceImpl.setDatabaseName(annotation.value())。

那AbstractRoutingDataSourceImpl是如何实现数据库切换的呢?查看AbstractRoutingDataSourceImpl的代码,overwrite中只有一些get(),set()方法。接着查看定义entityManagerFactoryBean的class,在这个class中定义了entityManagerFactoryBean,dataSource()这个bean中设置了默认default的database,当设置新的database后,完成数据库的切换。完整代码细节可查看demo3.

从前面的三个例子看,使用多个数据源时,Entity和Repository不用修改。只要设置好@EnableJpaRepositories,就能让Repository找到正确的数据源,自动装配。其中主要的两个参数是创建Entity的entityManagerFactoryRef和创建Repository的transactionManagerRef。为了创建EntityManagerFactory,我们需要借助DataSourceProperties创建DataSource对象。而使用@ConfigurationProperties,可以自动从配置文件生成DataSourceProperties。创建TransactionManager则需要指定SQL方言。方言类名可以通过Environment从配置中读取。从上面例子中可以看到,都是通过EntityManagerFactoryBuilder创建了LocalContainerEntityManagerFactoryBean,那LocalContainerEntityManagerFactoryBean的作用是什么呢?实际JPA 定义了两种类型的实体管理器:

应用程序管理类型(Application-managed):当应用程序向实体管理器工厂直接请求实体管理器时,工厂会创建一个实体管理器。在这种模式下,程序要负责打开或关闭实体管理器并在事务中对其进行控制。这种方式的实体管理器适合于不运行在 Java EE 容器中的独立应用程序。

容器管理类型(Container-managed):实体管理器由 Java EE 创建和管理。应用程序根本不与实体管理器工厂打交道。相反,实体管理器直接通过注入或 JNDI 来获取。容器负责配置实体管理器工厂。这种类型的实体管理器最适用于 Java EE 容器,在这种情况下会希望在 persistence.xml 指定的 JPA 配置之外保持一些自己对 JPA 的控制。

这对想使用 JPA 的 Spring 开发者来说又意味着什么呢?其实这并没太大的关系。不管你希望使用哪种 EntityManagerFactory,Spring 都会负责管理 EntityManager。如果你使用的是应用程序管理类型的实体管理器,Spring 承担了应用程序的角色并以透明的方式处理 EntityManager。在容器管理的场景下,Spring 会担当容器的角色。这两种实体管理器工厂分别由对应的 Spring 工厂 Bean 创建:

LocalEntityManagerFactoryBean 生成应用程序管理类型的 EntityManagerFactory;

LocalContainerEntityManagerFactoryBean 生成容器管理类型的 EntityManagerFactory。

EntityManagerFactoryBuilder采用构造者模式,传说数据库连接相关信息,即可完成对数据库的访问。

实现数据源切换用到了AbstractRoutingDataSource,该class包含的常见的属性以及含义如下:

targetDataSources是目标数据源集合

defaultTargetDataSource是默认数据源

resolvedDataSources是解析后的数据源集合

resolvedDefaultDataSource是解析后的默认数据源

要实现数据源切换,实际是自定义一个类扩展AbstractRoutingDataSource抽象类,相当于数据源DataSourcer的路由中介,可以实现在项目运行时根据相应key值切换到对应的数据源DataSource上。先看看AbstractRoutingDataSource的源码:

从源码可以看出AbstractRoutingDataSource继承了AbstractDataSource并实现了InitializingBean,AbstractRoutingDataSource的getConnection()方法调用了determineTargetDataSource()的该方法,这里重点看determineTargetDataSource()方法代码,方法里使用到了determineCurrentLookupKey()方法,它是AbstractRoutingDataSource类的抽象方法,也是实现数据源切换要扩展的方法,该方法的返回值就是项目中所要用的DataSource的key值,拿到该key后就可以在resolvedDataSource中取出对应的DataSource,如果key找不到对应的DataSource就使用默认的数据源。

自定义类扩展AbstractRoutingDataSource类时就是要重写determineCurrentLookupKey()方法来实现数据源切换功能,具体代码如下所示:

以上就是对于多数据库切换的介绍。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/377371.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

FFmpeg 中的多线程解码

1.共享变量的互斥互斥锁(mutex-lock)是一种信号量,用来防止两个线程在同一时刻访问相同的共享资源,它有锁定状态和非锁定状态。在任意时刻,一个线程要想存取共享数据,线程必须首先获得mutex-lock&#xff0…

一种全新的图像变换理论的实验(四)——研究目的替代DCT和小波

一、前言 2023年02月28日凌晨1点 以前我定义为这个算法是滤波算法,实则上应该算是一种新变换算法,比如傅里叶变换(FFT)、离散余弦变换(DCT),以及小波变换。所以就把所有的标题改变了一下。 本次…

MySQL的InnoDB 三种行锁,SQL 语句加了哪些锁?

InnoDB 三种行锁: Record Lock(记录锁):锁住某一行记录 Gap Lock(间隙锁):锁住一段左开右开的区间 Next-key Lock(临键锁):锁住一段左开右闭的区间 哪些语句…

前端面试题 —— HTML

目录 一、src 和 href 的区别 二、对 HTML 语义化的理解 三、DOCTYPE(⽂档类型) 的作⽤ 四、script 标签中 defer 和 async 的区别 五、常⽤的 meta 标签有哪些? 六、HTML5 有哪些更新 八、行内元素有哪些?块级元素有哪些? 空(void)元素…

【正点原子FPGA连载】第十九章FreeRtos Hello World实验 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南

1)实验平台:正点原子MPSoC开发板 2)平台购买地址:https://detail.tmall.com/item.htm?id692450874670 3)全套实验源码手册视频下载地址: http://www.openedv.com/thread-340252-1-1.html 第十九章FreeRto…

项目调研丨以太坊再质押项目EigenLayer白皮书四大看点(内附完整版中文白皮书)

北京时间2月21日下午,被众多一线投研机构视为2023年以太坊最重要的创新,有可能开启以太坊新叙事方向的项目Eigenlayer终于披露了其第一版白皮书。EigenLayer是以太坊的再质押集,允许共识层ETH质押者选择验证构建在以太坊生态系统之上的新软件…

第七节 面向对象

面向对象 1.类和对象是什么? )类:是共同特征的描述(设计图);对象:是真实存在的具体实例。 2.如何设计类? public class 类名 { 1、成员变量(代表属性的,一般是名词) 2、成员方法(代表行为的,一般是动词) 3.如何创建对象? 类名对象名new 类名(); 4.拿到对象后怎么…

ubuntu 编译安装支持CUDA的OpenCV

安装须知 cuda支持 在安装完“ linux CUDAtoolkitcudnntensorrt 的安装”之后进行支持cuda的opencv安装 否则报错:CMake Error at modules/dnn/CMakeLists.txt:41 (message): DNN: CUDA backend requires CUDA Toolkit. Please resolve dependency or disable OPE…

Ubuntu 安装指定版本 Mysql,并设置远程连接(以安装mysql 5.5 为例)

目录 一、安装Mysql 1、卸载Mysql(可跳过) 2、安装mysql 软件源 3、安装mysql 5.5 4、验证测试 二、设置远程登录 1、允许使用root账号远程连接 2、Mysql 允许远程登录 一、安装Mysql 1、卸载Mysql(可跳过) 如果之前安装…

数据结构六大排序

1.插入排序 1.插入排序 思路: 从第一个元素开始认为是有序的,去一个元素tem从有序序列从后往前扫描,如果该元素大于tem,将该元素一刀下一位,循环步骤3知道找到有序序列中小于等于的元素将tem插入到该元素后&#xff0…

卡特兰数

文章目录1、简介1.1 何为卡特兰数1.2 卡特兰数的通项公式2、应用2.1 题目1:括号合法题目描述思路分析2.2 题目2:进出栈的方式2.2.1 题目描述2.2.2 思路分析2.3 题目3:合法的序列2.3.1 题目描述2.3.2 思路分析2.3.3 代码实现2.4 题目4&#xf…

分布式ID生成系统

目录背景常用分布式ID生成系统介绍UUIDSnowflake背景 在大多数复杂的分布式系统中,往往需要对大量的数据和消息进行唯一标识。而对分布式系统后台数据库的分库分表后需要有一个唯一的ID来表示一条数据或者是消息。那么我们分布式系统ID一般都有哪些需求呢&#xff1…

IP地址、主机名、域名解析(DNS)

1.什么是IP地址 每一台联网的电脑都会有一个地址,用于和其他计算机进行通讯 IP地址主要有两个版本:v4 v6 IPV4版本的地址格式名为:a.b.c.d,其中abcd表示0-225的数字,如192.168.88.10为一个标准地址 查看IP地址&#x…

Android 蓝牙开发——HCI log 分析(二十)

HCI log 是用来分析蓝牙设备之间的交互行为是否符合预期,是否符合蓝牙规范。对于蓝牙开发者来说,通过 HCI log 可以帮助我们更好地分析问题,理解蓝牙协议。 一、抓取HCI log 1、手机抓取HCI log 在开发者选项中打开启用蓝牙HCI信息收集日志开关,Android系统就开始自动地收…

计算机SCI论文选题和投稿需要注意什么? - 易智编译EaseEditing

科研创新与选题 科研创新是至关重要的,往往关系到论文是否顺利发表。 摆在我们面前的,往往是别人挑剩下的资料,似乎毫无写作价值,很多人便知难而退,干脆不写论文了。 其实,应该问问自已“我有什么&#xf…

Flask应用的基本组成部分、模板引擎Jinja2的使用、Flask-WTF、SQLAlchemy

目录标题1. Flask应用的基本组成部分1.1 路由(Routing)1.2 视图函数(View Function)1.3 请求(Request)1.4 响应(Response)2. 模板引擎Jinja2的使用2.1 入门案例2.2 条件判断2.3 循环…

Python采集m3u8格式做个小姐姐动态壁纸~

人生苦短,我用python 首先,我和大家一样喜欢看小姐姐~ 其次,看美丽的事物会让人更加有动力去… 我编不下去了哈哈哈,我就是爱看充满美感的人儿~ 更多python好看的:点击此处跳转文末名片获取 环境 Pythonpycharm 模块使用 第…

使用virtualenv和pip构建项目所需的独立Python环境

1、为什么需要独立的Python环境?在讲技术前,想先讲讲目的。为什么我们需要独立的Python环境?这里就借用virtualenv的文档来解释吧。virtualenv is a tool to create isolated Python environments.The basic problem being addressed is one …

51-Jenkins-Periodic Backup插件实现Jenkins备份

Periodic Backup插件实现Jenkins备份前言目录结构插件备份安装插件使用插件前言 本篇来学习下使用Periodic Backup插件实现Jenkins备份 目录结构 Jenkins的所有数据都是存放在文件中的,所以,Jenins备份其实就是备份Jenkins_HOME目录。 Jenkins_Home目…

taobao.user.buyer.get( 查询买家信息API )

¥开放平台基础API必须用户授权 查询买家信息API,只能买家类应用调用。 公共参数 请求地址: HTTP地址 http://gw.api.taobao.com/router/rest 公共请求参数: 公共响应参数: 请求参数 响应参数 点击获取key和secret 请求示例 TaobaoClient client new…