怎样做才是最优雅方式切换 web 项目数据源 ?
随着业务变迁/需求变更,JavaEE 应用中会被迫连接多个数据源进行业务处理。
怎样在不影响原有项目结构的情况下,已最优雅/最简洁的方式动态切换数据源呢?
本文已一次添加数据源后动态切换实践为例,描述整个思考和实践过程,文中如有纰漏,还望指正。
1. 依赖 Spring 动态数据源实现
Spring 中提供了一个叫做 AbstractRoutingDataSource (抽象路由数据源)继承自 AbstractDataSource 并实现了 JDK DataSource 接口。
也就意味着继承 AbstractRoutingDataSource 并重写它 determineCurrentLookupKey 方法的类可以作为数据源,并个性化多数据源动态路由切换。
(如果你平时够仔细的话,现开源的数据库连接池都实现 DataSource 接口并进行了自己的个性化封装。)
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected 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 一下。
怎样做才是最优雅方式切换 web 项目数据源 ?的更多相关文章
- Jar包方式运行web项目
使用Maven进行打包 在自己的电脑终端中进入到pom.xml文件的目录中执行maven打包.命令为: mvn clean package 1 成功的标志为上面显示BUILD SUCCESS成功打包 ...
- 在基于MVC的Web项目中使用Web API和直接连接两种方式混合式接入
在我之前介绍的混合式开发框架中,其界面是基于Winform的实现方式,后台使用Web API.WCF服务以及直接连接数据库的几种方式混合式接入,在Web项目中我们也可以采用这种方式实现混合式的接入方式 ...
- 用idea简单创建web项目——两种方式
最近同学让我教她们用idea创建web项目,于是我用两种方式创建web项目,并整理截图给她们看,一种是用maven创建,一种是不用maven创建,适合菜鸟哈哈~ 方法一:不用maven 1.解压tom ...
- 代码首要的目标应该是“解决问题”(包括“没有 bug”),其次的目标才是“简单优雅”。
什么是现实理想主义者 曾经有人看了我的文章,以为我是一个“理想主义者”,来找我聊天.他说:“你知道吗,我跟你一样喜欢简单优雅的代码.上次我在某公司工作,看到他们的代码乱得不成样子,二话没说给他们重写了 ...
- iOS APP 如何做才安全
本来 写了一篇<iOS 如何做才安全--逆向工程 - Reveal.IDA.Hopper.https抓包 等>,发现文章有点杂,并且“iOS 如何做才安全”这部分写的越来越多,觉得 分出来 ...
- 【转】iOS-APP如何做才安全
iOS应用的安全性 常常被大家忽视. iOS 如何做才安全: 1.首先,我们可以通过iTunes 下载 AppStore的ipa文件(苹果 把开发者上传的ipa包 进行了加壳再放到AppStore中) ...
- SpringBoot项目整合Retrofit最佳实践,这才是最优雅的HTTP客户端工具!
大家都知道okhttp是一款由square公司开源的java版本http客户端工具.实际上,square公司还开源了基于okhttp进一步封装的retrofit工具,用来支持通过接口的方式发起http ...
- 以正确的方式开源 Python 项目
以正确的方式开源 Python 项目 大多数Python开发者至少都写过一个像工具.脚本.库或框架等对其他人也有用的工具.我写这篇文章的目的是让现有Python代码的开源过程尽可能清 晰和无痛.我不是 ...
- 三、自动化测试平台搭建-django-如何用mysql数据库做web项目
从这节开始到后面说的大概内容如下: 这里说的是Django做一个web项目的大概框架,从下篇具体说Django中的模型(查询..),视图(请求,响应,cookie,session..),模板(验证码, ...
随机推荐
- 10.application对象
1.application对象实现了用户数据的共享,可存放全局变量 2.application开始于服务器的启动,终止于服务器的关闭. 3.在用户的前后连接或不同用户之间的连接中,可以对applica ...
- 用py2exe将python文件转换成exe可执行程序
1.首先需要安装py2exe模块,下载地址:http://www.lfd.uci.edu/~gohlke/pythonlibs/ 然后用pip install 命令安装py2exe模块,如果你用的py ...
- WriteTeacherObj
package JBJADV003;import java.io.*;public class WriteTeacherObj { /** * @param args */ public static ...
- Jmeter之分布式测试
1)Jmeter 是纯java 应用,对于CPU和内存的消耗比较大,并且受到JVM的一些限制: 一般情况下,依据机器配置,单机的发压量为300-600,因此,当需要模拟数以千计的并发用户时,使用单台机 ...
- java IO之 编码 (码表 编码 解码 转换流)
编码 什么是编码? 计算机中存储的都是二进制,但是要显示的时候,就是我们看到的却可以有中国 ,a 1 等字符 计算机中是没有存储字符的,但是我们却看到了.计算机在存储这些信息的时候,根据一个有规 则 ...
- 欢迎大家Follow me!微软MVP罗勇(Dynamics CRM方向)欢迎您!
我是一名八零后,来自湖南乡村,2002年毕业于大连大学工商管理专业,主要靠自学走上了编程之路.从2012年开始接触Dynamics CRM 2011,一直从事Dynamics CRM方面工作,熟悉Dy ...
- CSS3-loading动画(四)
图片看的效果真是不行,还是戳下面网址看吧 在线示例:http://liyunpei.xyz/loading.html 十七.效果十七 三个小球,纵向居中,间距撑开,依次改变小球的translateY的 ...
- 【ALB学习笔记】基于.NET环境的高频RFID卡读写设备的基本操作案例
基于.NET环境的高频RFID卡读写设备的基本操作案例 广东职业技术学院 欧浩源 1.引言 RFID高频卡在我们的日常生活中随处可见,是物联网应用中不可或缺的一个重要部分,也是全国职业院校技能大赛& ...
- LoadRunner接口工作总结
因为工作中需要开发维护类似枢纽性质的平台,所以经常利用LR进行接口测试.接口自动化测试.接口压力测试.用多了LR,有点不愿意使用报文编辑器进行手工接口测试了. 接口脚本操作过程: 首先:打开LR,N ...
- Java8学习(3)- Lambda 表达式
猪脚:以下内容参考<Java 8 in Action> 本次学习内容: Lambda 基本模式 环绕执行模式 函数式接口,类型推断 方法引用 Lambda 复合 上一篇Java8学习(2) ...