怎样做才是最优雅方式切换 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,如需转载请自行联系原作者

相关文章
|
安全 Java API
Java Web 在线商城项目最新技术实操指南帮助开发者高效完成商城项目开发
本项目基于Spring Boot 3.2与Vue 3构建现代化在线商城,涵盖技术选型、核心功能实现、安全控制与容器化部署,助开发者掌握最新Java Web全栈开发实践。
966 1
|
JavaScript Java 微服务
现代化 Java Web 在线商城项目技术方案与实战开发流程及核心功能实现详解
本项目基于Spring Boot 3与Vue 3构建现代化在线商城系统,采用微服务架构,整合Spring Cloud、Redis、MySQL等技术,涵盖用户认证、商品管理、购物车功能,并支持Docker容器化部署与Kubernetes编排。提供完整CI/CD流程,助力高效开发与扩展。
1309 64
|
人工智能 安全 程序员
用 Colab 和 ngrok 免费部署你的 Web UI 项目,随时随地访问!
用 Colab 和 ngrok 免费部署你的 Web UI 项目,随时随地访问!
1883 12
|
安全 JavaScript Java
java Web 项目完整案例实操指南包含从搭建到部署的详细步骤及热门长尾关键词解析的实操指南
本项目为一个完整的JavaWeb应用案例,采用Spring Boot 3、Vue 3、MySQL、Redis等最新技术栈,涵盖前后端分离架构设计、RESTful API开发、JWT安全认证、Docker容器化部署等内容,适合掌握企业级Web项目全流程开发与部署。
1242 0
|
监控 安全 测试技术
如何在实际项目中应用Python Web开发的安全测试知识?
如何在实际项目中应用Python Web开发的安全测试知识?
368 61
|
网络协议 Java Shell
java spring 项目若依框架启动失败,启动不了服务提示端口8080占用escription: Web server failed to start. Port 8080 was already in use. Action: Identify and stop the process that’s listening on port 8080 or configure this application to listen on another port-优雅草卓伊凡解决方案
java spring 项目若依框架启动失败,启动不了服务提示端口8080占用escription: Web server failed to start. Port 8080 was already in use. Action: Identify and stop the process that’s listening on port 8080 or configure this application to listen on another port-优雅草卓伊凡解决方案
1320 7
|
安全 Linux 开发工具
零基础构建开源项目OpenIM桌面应用和pc web- Electron篇
OpenIM 为开发者提供开源即时通讯 SDK,作为 Twilio、Sendbird 等云服务的替代方案。借助 OpenIM,开发者可以构建安全可靠的即时通讯应用,如 WeChat、Zoom、Slack 等。 本仓库基于开源版 OpenIM SDK 开发,提供了一款基于 Electron 的即时通讯应用。您可以使用此应用程序作为 OpenIM SDK 的参考实现。本项目同时引用了 @openim/electron-client-sdk 和 @openim/wasm-client-sdk,分别为 Electron 版本和 Web 版本的 SDK,可以同时构建 PC Web 程序和桌面应用(Wi
1846 2
|
中间件 Go API
Go语言中几种流行的Web框架,如Beego、Gin和Echo,分析了它们的特点、性能及适用场景,并讨论了如何根据项目需求、性能要求、团队经验和社区支持等因素选择最合适的框架
本文概述了Go语言中几种流行的Web框架,如Beego、Gin和Echo,分析了它们的特点、性能及适用场景,并讨论了如何根据项目需求、性能要求、团队经验和社区支持等因素选择最合适的框架。
1997 1

热门文章

最新文章