当前位置: 首页 > 编程日记 > 正文

怎样做才是最优雅方式切换 web 项目数据源 ?

   随着业务变迁/需求变更,JavaEE 应用中会被迫连接多个数据源进行业务处理。

   怎样在不影响原有项目结构的情况下,已最优雅/最简洁的方式动态切换数据源呢?

   本文已一次添加数据源后动态切换实践为例,描述整个思考和实践过程,文中如有纰漏,还望指正。

1. 依赖 Spring  动态数据源实现

   

   Spring 中提供了一个叫做 AbstractRoutingDataSource (抽象路由数据源)继承自 AbstractDataSource 并实现了 JDK DataSource 接口。

   也就意味着继承 AbstractRoutingDataSource  并重写它 determineCurrentLookupKey 方法的类可以作为数据源,并个性化多数据源动态路由切换。

   (如果你平时够仔细的话,现开源的数据库连接池都实现 DataSource 接口并进行了自己的个性化封装。)

public class DynamicDataSource extends AbstractRoutingDataSource {@Overrideprotected Object determineCurrentLookupKey() {return DbContextHolder.getDbType();}
}

   对于一次 web 请求来说可以理解为单独的线程,将当前数据源暂存在线程当中是比较合理的做法。

public class DbContextHolder {private static final ThreadLocal contextHolder = new ThreadLocal<>();/*** 设置数据源** @param dbSourceEnum 要设置的数据库枚举名称*/public static void setDbType(DBSourceEnum dbSourceEnum) {contextHolder.set(dbSourceEnum.getValue());}/*** 取得当前数据源*/public static String getDbType() {return String.valueOf(contextHolder.get());}/*** 清除上下文数据*/public static void clearDbType() {contextHolder.remove();}
}

   当然为了后期的扩展和维护,以及使用的便捷性,这里数据源对象我们引入枚举类型。

   这样后续其他同事编程使用枚举,改动起来也相当方便,还能进行数据源的一些自定义说明。

public enum DBSourceEnum {one("dataSource1"),two("dataSource2");private String value;DBSourceEnum(String value) {this.value = value;}public String getValue() {return value;}
}

   上述的 dataSource1/dataSource2 即为 spring-context  中已加载的数据源对象 Id。

    <bean name="dataSource1" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">......</bean>
    <bean name="dataSource2" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">......</bean>

   接下来在 context 中配置继承自 AbstractRoutingDataSource 的 DynamicDataSource。

   <bean id="dataSource" class="com.rambo.spm.core.multidb.DynamicDataSource"><property name="targetDataSources"><map key-type="java.lang.String"><entry key="dataSource1" value-ref="dataSource1"/><entry key="dataSource2" value-ref="dataSource2"/></map></property><property name="defaultTargetDataSource" ref="dataSource1"/></bean>

   Ok,这样在配置后续 dao 层时使用该 DynamicDataSource 即可。

2. 最优雅的切换数据源方式

   完成上述工作之后,其实动态切换数据源已经实现,在业务层如下面这样编程。

        DbContextHolder.setDbType(DBSourceEnum.one);List<Menu> menuList = menuService.selectList(null);DbContextHolder.setDbType(DBSourceEnum.two);List<User> userList = userService.selectList(null);

   缺点很明显,连接数据源2时要进行切换/不利于扩展/切换不当时给同事埋雷的几率很大。

   和团队进行交流时,讨论出用强大 aop 来拦截 dao 层对象,动态切换数据源的方案。

   对于 dao 层对象来说访问数据库的哪张表是确定的,编写自定义注解与 dao 层对象进行绑定。

   自定义数据源注解如下:

@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface DataSource {DBSourceEnum value() default DBSourceEnum.one;
}

   编写切面处理对象,在 dao 层对象使用前进行拦截,顺手切换数据源,如果没有数据源注解,设置为默认。

   所以对于项目中原配数据源 dao 层对象,不需要进行任何修改,切面处理如下。

    @Before("cut()")public void doBefore(JoinPoint joinPoint) {DataSource dataSource = joinPoint.getTarget().getClass().getAnnotation(DataSource.class);DbContextHolder.setDbType(dataSource != null ? dataSource.value() : DBSourceEnum.one);log.info("当前数据源为:" + DbContextHolder.getDbType());}

   多数项目中 dao 层错综复杂的抽象和继承关系会给你 aop 切面拦截造成一定的困难,多思考、多实践总会有办法的。

   好了,就这样吧,有没有感觉 aop 拦截方式比在程序中硬编码更容易扩展、更容易编程、更容易理解,当然也更优雅。

   由此可扩展数据库方面很多,比如读写分离/分库分区....具体场景具体分析吧。

   代码已托管在:https://git.oschina.net/LanboEx/spmvc-mybatis.git 有兴趣的朋友,可以检到本地 run 一下。

  

相关文章:

C#开发编码规范

C#开发编码规范 注记&#xff1a;Pascal 大小写形式——所有单词第一个字母大写&#xff0c;其他字母小写。Camel 大小写形式——除了第一个单词&#xff0c;所有单词第一个字母大写&#xff0c;其他字母小写。类名使用Pascal大小写形式 public class HelloWorld{ …}方法使用…

【原创】如何分析一个网站使用的服务器类型

如何知道一个网站使用的是什么类型的服务器呢&#xff1f;如其前端使用的是apache呢还是iis呢还是nginx呢还是其他呢&#xff1f;中间件使用的是什么呢&#xff1f;有以下几种方法&#xff1a; 首先说原理&#xff1a;http协议的头部提供了丰富的信息告诉我们一些信息&#xff…

想学Python?那这套教程再适合你不过了!!

如果你想问最近这些年什么编程语言最值得学习&#xff0c;我相信很多人都会告诉你是Python&#xff01;所以不仅是开发小白&#xff0c;甚至很多开发老手&#xff0c;也都开始学习Python&#xff0c;作为辅助第二语言来提高自己的职场竞争力。不过结合我最近这些年Python的学习…

Java compiler level does not match the version of the installed Java project facet.

2019独角兽企业重金招聘Python工程师标准>>> 解决方式&#xff1a;右击项目点击>>选择对应的版本就好、&#xff01; 转载于:https://my.oschina.net/liusonghuang/blog/1512115

【C#小知识】C#中一些易混淆概念总结(三)---------结构,GC,静态成员,静态类...

目录&#xff1a; 【C#小知识】C#中一些易混淆概念总结 【C#小知识】C#中一些易混淆概念总结&#xff08;二&#xff09; ---------------------------------------分割线---------------------------------------------- 一&#xff0c;C#中结构 在C#中可以使用struct关键字来…

C#精髓【月儿原创】第一讲 使用垃圾回收器

说明&#xff1a;准备出一个系列&#xff0c;所谓精髓讲C#语言要点。这个系列没有先后顺序&#xff0c;不过尽量做到精。可能会不断增删整理&#xff0c;本系列最原始出处是csdn博客,谢谢关注。 C#精髓 第一讲 使用垃圾回收器 作者&#xff1a;清清月儿 主页&#xff1a;h…

5GtoB即将迎来规模商用,如何共创行业新价值?

4月14日&#xff0c;在华为公司第18届全球分析师大会期间&#xff0c;华为携手运营商代表、产业界代表举办了“5G激发行业新价值”论坛&#xff0c;与行业分析师、金融分析师共同探讨对5GtoB产业未来的展望、创新解决方案以及最佳业务实践。 5G发展进入快车道&#xff0c;融合…

$.ajax上传文件或者上传图片

2019独角兽企业重金招聘Python工程师标准>>> ###FormData 想得到一个FormData对象就必须new一个FormData对象&#xff0c;然后使用append()方法向该对象里添加键值对 var formdata new FormData() //包含用户选择文件的文件流 formdata.append("file",ev…

BMP图形文件分析类(c#)

1using System; 2using JJBase.FILE; 3namespace JJBase.Image 4{ 5 /**//// <summary> 6 /// BMP 的摘要说明。 7 /// </summary> 8 public class BMP 9 { 10 11 public BMP() 12 { 13 // 14 …

gsoap 学习 1-由wsdl文件生成h头文件

开始前先看一下用户向导吧 http://www.cs.fsu.edu/~engelen/soap.html 中左侧点击Documentation 英语水平确实有限,有些内容可能说的不准确,敬请参考向导中原文,以免误导 向导 1.1节中提供了gSoap开发包的下载地址http://sourceforge.net/projects/gsoap2 下载最新gSoap程序包解…

微软每年豪砸安全研发 10 亿美元,聊聊背后的技术密码

从无序中寻找踪迹&#xff0c;从眼前事探索未来。正值 IT 黄金十年新开端&#xff0c; CSDN 欲以中立技术社区专业、客观的角度&#xff0c;深度探讨中国前沿 IT 技术演进&#xff0c;现在推出年度重磅企划——「拟合」&#xff0c;通过对话企业高管大咖&#xff0c;跟踪报道企…

phpstudy多站点配置好后index of/ 列表无法出现的解决

打开配置文件vhost-conf&#xff0c;看到图中画黄色线部分。把新配置的站点中的options后面加上 Indexes 就行了。 转载于:https://www.cnblogs.com/wubuwei/p/7388700.html

【OSX】build AOSP 2.3.7时的build error解决

原始的error log&#xff1a; 1 2 PLATFORM_VERSION_CODENAMEREL3 PLATFORM_VERSION2.3.74 TARGET_PRODUCTgeneric5 TARGET_BUILD_VARIANTeng6 TARGET_SIMULATOR7 TARGET_BUILD_TYPErelease8 TARGET_BUILD_APPS9 TARGET_ARCHarm 10 HOST_ARCHx86 11 HOST_OSdarwin 12 HOST_BUIL…

undefined reference to 'pthread_create'

pthread 库不是 Linux 系统默认的库&#xff0c;连接时需要使用静态库 libpthread.a&#xff0c;所以在使用pthread_create()创建线程&#xff0c;以及调用 pthread_atfork()函数建立fork处理程序时&#xff0c;需要链接该库。源文件&#xff0c;不要忘了加上头文件#include<…

瞄准自然语言处理,百度与鹏城实验室共建实验室

4月16日&#xff0c;百度与鹏城“自然语言处理联合实验室”签约暨揭牌仪式在鹏城实验室举行。 中国工程院院士、鹏城实验室主任高文出席仪式并致辞&#xff0c;鹏城实验室人工智能研究中心主任李革教授和百度集团首席技术官王海峰代表双方介绍了联合实验室前期筹备情况并签署协…

PostgreSQL SQL 语言:并行查询

本文档为PostgreSQL 9.6.0文档&#xff0c;本转载已得到原译者彭煜玮授权。 1. 并行查询如何工作 当优化器判断对于某一个特定的查询&#xff0c;并行查询是最快的执行策略时&#xff0c;优化器将创建一个查询计划。该计划包括一个 Gather 节点。下面是一个简单的例子&#xff…

常用的linux的命令行操作

2019独角兽企业重金招聘Python工程师标准>>> 系统信息 arch 显示机器的处理器架构(1) uname -m 显示机器的处理器架构(2) uname -r 显示正在使用的内核版本 dmidecode -q 显示硬件系统部件 - (SMBIOS / DMI) hdparm -i /dev/hda 罗列一个磁盘的架构特性 hdparm -tT …

Python 极简实现 IoU

来源 | 简明AI头图 | 下载于视觉中国出品 | AI 科技大本营&#xff08;ID&#xff1a;rgznai100&#xff09;IOU中文名叫交并比&#xff0c;见名知意就是交集与并集的比值。是在目标检测中常用的算法。IoU原理如上图所示&#xff0c;就是计算上面阴影部分与下面阴影部分的比值。…

静态页htm传参数

//从转向过来的URL中截取参数 开始function SplitUrl(key){var fstrkey;var getstr;var urldocument.URL.toString();urlurl.toLowerCase();//转为小写var locurl.indexOf(fstr);if(loc>0){getstrurl.substring(locfstr.length,url.length);return getstr;}else{return &quo…

vue router 入门笔记

vue router 入门笔记 tips&#xff1a; components优先级大于component&#xff0c;即当一个route对象里同时配置了component和components时component视为无效即使route对象有name属性&#xff0c;也要配置一个path属性。name属性只是配对用的&#xff0c;path是要直接打到url上…

用C#的Raw Socket实现网络封包监视

<script language"javascript" src"/ad/js/edu_left_300-300.js" type"text/javascript"></script> 谈起socket编程&#xff0c;大家也许会想起QQ和IE&#xff0c;没错。还有许多网络工具如P2P、NetMeeting等在应用层实现的应用程序…

人工智能是否能开启人类世界新纪元?

想必许多人都不止一次地幻想过&#xff0c;当人工智能发展到极限时&#xff0c;它将为我们的生活带来多少触手可及的便捷&#xff0c;或是意想不到的惊喜呢&#xff1f;试想一下我们身处英剧《黑镜》的世界中&#xff0c;人类的生活里充斥着人工智能对社交行为的各种评分机制&a…

1月国内操作系统市场:Windows XP份额高达60.84%

IDC评述网&#xff08;idcps.com&#xff09;02月21日报道&#xff1a;据CNZZ数据&#xff0c;在国内操作系统市场上&#xff0c;2014年1月份&#xff0c;微软Windows系统依旧称霸市场&#xff0c;份额为90.63%&#xff0c;环比去年末下滑0.57%。对于市场份额下滑一事&#xff…

17.08.17

控制文件 丢失部分控制文件&#xff1a; SQL> select * from v$controlfile; $ >/u01/app/oracle/oradata/orcl/control01.ctl SQL> select * from v$tablespace; 报错 SQL> alter system checkpoint; 报错 $ vi /u01/app/oracle/diag/rd…

用C#实现基于TCP协议的网络通讯

TCP协议是一个基本的网络协议&#xff0c;基本上所有的网络服务都是基于TCP协议的&#xff0c;如HTTP,FTP等等&#xff0c;所以要了解网络编程就必须了解基于TCP协议的编程。然而TCP协议是一个庞杂的体系&#xff0c;要彻底的弄清楚它的实现不是一天两天的功夫&#xff0c;所幸…

Java NIO系列教程(二) Channel

为什么80%的码农都做不了架构师&#xff1f;>>> Java NIO的通道类似流&#xff0c;但又有些不同&#xff1a; 既可以从通道中读取数据&#xff0c;又可以写数据到通道。但流的读写通常是单向的。通道可以异步地读写。通道中的数据总是要先读到一个Buffer&#xff0…

百度CTO王海峰博鳌解读AI“融合创新”,算力算法数据发挥综合作用

4月18至21日&#xff0c;博鳌亚洲论坛2021年年会在海南博鳌举行。19日下午&#xff0c;百度CTO王海峰受邀参加本届博鳌年会“后疫情时代的人工智能”为主题的圆桌论坛。与公钥加密技术之父、图灵奖得主惠特菲尔德迪菲&#xff0c;阿斯利康公司董事长雷夫约翰森等多位专家和企业…

Java开发工具(Eclipse中内容辅助键的使用)

* A:Alt/ 起提示作用* B:mainalt/,sysoalt/,给出其他提示* C:补充输出语句,选中需要输出的部分,alt/选择最后一项即可* C:定义自己的alt / * windows--perference-Java-Editor-Templates--New * A:新建 ctrl n(new)* B:格式化 ctrlshiftf(format)* C:导入包 ctrlshifto *…

常用的css3的新属性

2019独角兽企业重金招聘Python工程师标准>>> 作为前端开发人员&#xff0c;如果你还不知道或还没有接触过CSS3&#xff0c;那么你真的OUT了&#xff01;就像蒸汽机的发明标志工业革命的开始一样&#xff0c;CSS3和HTML5的出现&#xff0c;将会带来WEB前端开发以及互…

高效分页存储过程

存储过程与页面调用如下&#xff1a; CREATE PROCEDURE search_sptblName varchar(255), -- 表名 strGetFields varchar(1000) *, -- 需要返回的列 fldName varchar(255), -- 排序的字段名 PageSize int 10, -- 页尺寸 PageIndex int , -- 页码 doCount bit 0, -- 返回记录…