从单数据源到多数据源的探讨

今天我想简单地分享一下如何将一个老项目从单数据源切换为多数据源的过程。这个项目是一个使用 WAR 部署的传统 JSP Web 项目,运行在 JDK 1.7 环境下,项目中并没有使用 Spring Boot,而仅仅采用了 Spring MVC 框架。我的主要任务是将原本使用单一数据源的架构,升级为支持多数据源的架构。

为此,首先需要梳理清楚当前项目的模块依赖和数据源的使用情况,了解项目中所有的模块和类是如何引用和交互的,特别是涉及到数据库操作的部分。

引用排查

我也很直接,直接找到datasource关键字直接全局搜索,找到了很多引用地方,有些确实是命名不是很规范,并不是数据源也起名叫了这个名字,第一步直接去除没有用的相关类,做一个简单的筛除。如图所示:

接下来,我将剩余的引用部分划分为三个主要部分,具体如下:第一部分是与XML配置相关的内容。由于该项目是一个较为传统的Spring MVC老项目,因此所有的Bean依赖关系都是在XML文件中显式配置的。这一部分的工作主要是分析和梳理XML配置文件中与Bean定义及依赖注入相关的内容。

第二部分是Java引用的相关内容。对于一些XML中配置好的Bean,这些配置会被注入到Java类的相应位置,并在运行时使用。因此,这一部分需要重点关注那些通过XML配置注入的Bean以及它们在Java代码中的应用场景。

最后第三部分是关于properties配置文件的检查。需要检查是否有单独的配置项存在于properties文件中,这些配置项可能会影响系统的某些行为或参数设置。

业务梳理

这部分不太好说,需要自己对整个项目有所掌握才可以,要不然会让自己看的头疼,这部分看的时候,大概想了一下为什么这里这么用,这里用到数据源做了哪些业务,如果切换成多数据源后,应该如何处理。

我大概看了一下有基本下面几种情况:

  1. 注入数据源,直接生成jdbctemplate对象后,在代码里写业务逻辑执行SQL,看的头疼~~
  2. 注入到sqlsessionfactorybean中,集成到mybatis中。
  3. 使用现成的spring-security,注入数据源后,直接查询各种权限信息。
  4. 国际化配置使用到了数据源信息。

目前就这几种,因为项目使用的是jndi的方式注入,所以对于多数据源来说也有一些困难。不过我的大概思路就是将数据源注入个默认数据源,使用动态key的方式切换数据源。

后期思路

比如,配置文件首先就需要有多个数据源的信息,如下所示:

xml 复制代码
<bean id="dataSource1" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
  <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
  <property name="url" value="jdbc:mysql://localhost:3306/db1"/>
  <property name="username" value="user1"/>
  <property name="password" value="pass1"/>
</bean>

<bean id="dataSource2" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
  <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
  <property name="url" value="jdbc:mysql://localhost:3306/db2"/>
  <property name="username" value="user2"/>
  <property name="password" value="pass2"/>
</bean>

<bean id="sqlSessionFactory" class="ReloadableSqlSessionFactoryBean">
    <property name="targetDataSources">
        <map key-type="java.lang.String">
            <entry key="dataSource1" value-ref="dataSource1"/>
            <entry key="dataSource2" value-ref="dataSource2"/>
        </map>
    </property>
</bean>

定义数据源路由

java 复制代码
public class DataSourceContextHolder {
    private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();

    public static void setDataSourceKey(String key) {
        contextHolder.set(key);
    }

    public static String getDataSourceKey() {
        return contextHolder.get();
    }

    public static void clearDataSourceKey() {
        contextHolder.remove();
    }
}

配置动态数据源

java 复制代码
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

public class DynamicDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        return DataSourceContextHolder.getDataSourceKey();
    }
}

集成到SqlSessionFactoryBean,在 ReloadableSqlSessionFactoryBean 中使用动态数据源:

java 复制代码
public class MySqlSessionFactoryBean extends SqlSessionFactoryBean implements DisposableBean {
    private static final Log log = LogFactory.getLog(MySqlSessionFactoryBean.class);

    private AbstractRoutingDataSource routingDataSource;

    public void setTargetDataSources(Map<Object, Object> targetDataSources) {
        if (routingDataSource == null) {
            routingDataSource = new DynamicDataSource();
        }
        routingDataSource.setTargetDataSources(targetDataSources);
        routingDataSource.setDefaultTargetDataSource(targetDataSources.values().iterator().next());
        super.setDataSource(routingDataSource);
    }

    @Override
    protected SqlSessionFactory buildSqlSessionFactory() throws Exception {
        return super.buildSqlSessionFactory();
    }

    @Override
    public void destroy() throws Exception {
        // 清理资源
    }
}

在需要切换数据源的地方调用 DataSourceContextHolder.setDataSourceKey("dataSource1") 或 DataSourceContextHolder.setDataSourceKey("dataSource2")。

总的来说,将传统单数据源架构迁移到多数据源架构并不简单,但通过合理的模块梳理和逐步推进,整个过程可以得到有效实施。


我是努力的小雨,一个正经的 Java 东北服务端开发,整天琢磨着 AI 技术这块儿的奥秘。特爱跟人交流技术,喜欢把自己的心得和大家分享。还当上了腾讯云创作之星,阿里云专家博主,华为云云享专家,掘金优秀作者。各种征文、开源比赛的牌子也拿了。

💡 想把我在技术路上走过的弯路和经验全都分享出来,给你们的学习和成长带来点启发,帮一把。

🌟 欢迎关注努力的小雨,咱一块儿进步!🌟

相关推荐
C++小厨神1 小时前
Java语言的循环实现
开发语言·后端·golang
Quantum&Coder2 小时前
Ruby语言的数据库编程
开发语言·后端·golang
ByteBlossom6662 小时前
Ruby语言的网络编程
开发语言·后端·golang
静水楼台x2 小时前
Java中json的一点理解
java·后端·json
macrozheng4 小时前
Jenkins+Docker一键打包部署项目!步骤齐全,少走坑路!
java·spring boot·后端·docker·jenkins
啊晚5 小时前
ASP.NET Core - 依赖注入(四)
后端·asp.net
云端 架构师5 小时前
PL/SQL语言的文件操作
开发语言·后端·golang
小蒜学长5 小时前
疾病防控综合系统设计与实现(代码+数据库+LW)
前端·数据库·vue.js·spring boot·后端·oracle
camellias_6 小时前
Springboot(五十八)SpringBoot3使用Redisson实现接口的限流功能
java·spring boot·后端
C++小厨神7 小时前
Java语言的软件工程
开发语言·后端·golang