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

简介:

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 拦截方式比在程序中硬编码更容易扩展、更容易编程、更容易理解,当然也更优雅。

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


本文转自Orson博客园博客,原文链接:http://www.cnblogs.com/java-class/p/7374623.html,如需转载请自行联系原作者

相关文章
|
5天前
|
JavaScript 前端开发 开发工具
web项目规范配置(husky、eslint、lint-staged、commit)
通过上述配置,可以确保在Web项目开发过程中自动进行代码质量检查和规范化提交。Husky、ESLint、lint-staged和Commitlint共同作用,使得每次提交代码之前都会自动检查代码风格和语法问题,防止不符合规范的代码进入代码库。这不仅提高了代码质量,还保证了团队协作中的一致性。希望这些配置指南能帮助你建立高效的开发流程。
18 5
|
11天前
|
JavaScript 前端开发 数据安全/隐私保护
Web开发者必看:手把手教你如何轻松播放m3u8流地址,解锁视频播放新技能,让你的项目更上一层楼!
【10月更文挑战第23天】随着互联网技术的发展,m3u8格式因良好的兼容性和高压缩率被广泛用于网络流媒体传输。本文介绍如何在Web端播放m3u8流地址,包括引入视频播放器(如Video.js)、创建播放器容器、初始化播放器及播放m3u8流的具体步骤。此外,还涉及处理加密m3u8流的示例。
34 1
|
20天前
|
JSON 搜索推荐 API
Python的web框架有哪些?小项目比较推荐哪个?
【10月更文挑战第15天】Python的web框架有哪些?小项目比较推荐哪个?
40 1
|
1月前
|
前端开发 JavaScript API
惊呆了!学会AJAX与Fetch API,你的Python Web项目瞬间高大上!
在Web开发领域,AJAX与Fetch API是提升交互体验的关键技术。AJAX(Asynchronous JavaScript and XML)作为异步通信的先驱,通过XMLHttpRequest对象实现了局部页面更新,提升了应用流畅度。Fetch API则以更现代、简洁的方式处理HTTP请求,基于Promises提供了丰富的功能。当与Python Web框架(如Django、Flask)结合时,这两者能显著增强应用的响应速度和用户体验,使项目更加高效、高大上。
47 2
|
2月前
|
前端开发 Python
前后端分离的进化:Python Web项目中的WebSocket实时通信解决方案
在现代Web开发领域,前后端分离已成为一种主流架构模式,它促进了开发效率、提升了应用的可维护性和可扩展性。随着实时数据交互需求的日益增长,WebSocket作为一种在单个长连接上进行全双工通讯的协议,成为了实现前后端实时通信的理想选择。在Python Web项目中,结合Flask框架与Flask-SocketIO库,我们可以轻松实现WebSocket的实时通信功能。
55 2
|
26天前
|
监控 Java Maven
springboot学习二:springboot 初创建 web 项目、修改banner、热部署插件、切换运行环境、springboot参数配置,打包项目并测试成功
这篇文章介绍了如何快速创建Spring Boot项目,包括项目的初始化、结构、打包部署、修改启动Banner、热部署、环境切换和参数配置等基础操作。
106 0
|
3月前
|
测试技术 开发者 Python
FastAPI的神奇之处:如何用Python引领Web开发的新浪潮,让你的项目一鸣惊人?
【8月更文挑战第31天】在现代软件开发中,Web应用至关重要,而FastAPI作为高性能Python Web框架,凭借简洁的语法与高效的开发体验,备受开发者青睐。本文将介绍FastAPI的基础概念、使用方法及最佳实践,涵盖路由、模板、请求对象等核心概念,并探讨其优势与社区扩展,助您高效构建Web应用。
96 1
|
3月前
|
存储 JSON 数据安全/隐私保护
"FastAPI身份验证与授权的奥秘:如何用Python打造坚不可摧的Web应用,让你的项目一鸣惊人?"
【8月更文挑战第31天】在现代Web开发中,保证应用安全性至关重要,FastAPI作为高性能Python框架,提供了多种身份验证与授权方式,包括HTTP基础认证、OAuth2及JWT。本文将对比这些机制并附上示例代码,展示如何使用HTTP基础认证、OAuth2协议以及JWT进行用户身份验证,确保只有合法用户才能访问受保护资源。通过具体示例,读者可以了解如何在FastAPI项目中实施这些安全措施。
130 1
|
3月前
|
存储 数据库 开发者
Web2py的神秘力量:如何用Python打造快速原型设计与开发,让你的项目一鸣惊人?
【8月更文挑战第31天】在现代软件开发中,快速原型设计至关重要。Web2py作为一款Python Web框架,凭借其简洁的语法和高效开发流程受到开发者青睐。本文通过在线调查问卷系统的案例,展示Web2py在快速原型设计中的应用,包括需求分析、数据库设计、表单创建及路由实现,并提供示例代码,帮助读者理解其最佳实践。
24 1
|
3月前
|
开发者 Java 安全
Struts 2 实战秘籍:Action 驱动业务,配置文件成就高效开发!
【8月更文挑战第31天】Struts 2 框架作为 Apache 软件基金会的顶级项目,广泛应用于企业级 Web 应用开发。其核心组件 Action 类处理用户请求,而配置文件定义请求与 Action 类间的映射关系。掌握 Action 组件的最佳实践包括继承 `ActionSupport` 类、实现 `execute` 方法及使用类型安全的方法;配置文件的最佳实践则涉及组织 Action 到包中、定义全局结果及使用通配符映射。遵循这些最佳实践,可构建高效、可维护的 Web 应用程序。
40 0

热门文章

最新文章