怎样做才是最优雅方式切换 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..),模板(验证码, ...
随机推荐
- 3.ubuntu如何安装搜狗输入法
1.http://blog.csdn.net/qq_21792169/article/details/53152700 2.http://jingyan.baidu.com/article/54b6b ...
- 2.如何修改apache的默认端口
打开apache的conf文件夹,找到server.xml,修改里面这段的port即可,重启apache,修改成功
- SVN仓库迁移到Git遇到的两个问题和解决办法
OS: CentOS 7.0 准备: git svn git-svn sudo yum install git sudo yum install subversion sudo yum install ...
- ASP.NET Core Web 资源打包与压缩
本文将介绍使用的打包和压缩的优点,以及如何在ASP.NET Core应用程序中使用这些功能. 概述 在ASP.Net中可以使用打包与压缩这两种技术来提高Web应用程序页面加载的性能.通过减少从服务器请 ...
- 霍尔开关MH253ESO在减压神器指尖手指陀螺中的作用
手指陀螺首先在欧美国家流行起来,现如今又在国外掀起一帆狂潮,它是一款排遣无聊的小玩具,又被称为减压神器. 其工作原理:是由一个双向的对称体作为主体,在主体中间嵌入一个轴承的设计组合,整体构成一个可平面 ...
- iOS 微信消息拦截插件系列教程-附录(服务端成果展示)
微信iOS消息拦截插件教程 标签: 越狱开发 背景介绍 本教程所有内容免费 本教程来源于一次知识分享,如果有需要了解更多的 请联系QQ:480071411 iOS逆向高级开发群:375024882 服 ...
- c# 中事务处理
1.在c#中,使用以下语句,就可以不用再数据库中进行事务语句处理了. TransactionOptions transactionOption = new TransactionOptions() ; ...
- window.onload 和 $(document).ready()
一. window.onload 1. 必须等到页面上所有元素(包括图片, JS文件,CSS文件等外部资源)加载完成后才执行 2. window.onload绑定多个函数时,只会执行最后一个 < ...
- Oracle系统表实用操作笔记
1.取得指定用户的所有表名: SQL1: SELECT OWNER AS "对象所有者", OBJECT_NAME AS "表名", OBJECT_ID AS ...
- Spring Boot 系列(四)静态资源处理
在web开发中,静态资源的访问是必不可少的,如:图片.js.css 等资源的访问. spring Boot 对静态资源访问提供了很好的支持,基本使用默认配置就能满足开发需求. 一.默认静态资源映射 S ...