今天我想简单地分享一下如何将一个老项目从单数据源切换为多数据源的过程。这个项目是一个使用 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 文件中,这些配置项可能会影响系统的某些行为或参数设置。
业务梳理
这部分不太好说,需要自己对整个项目有所掌握才可以,要不然会让自己看的头疼,这部分看的时候,大概想了一下为什么这里这么用,这里用到数据源做了哪些业务,如果切换成多数据源后,应该如何处理。
我大概看了一下有基本下面几种情况:
注入数据源,直接生成 jdbctemplate 对象后,在代码里写业务逻辑执行 SQL,看的头疼~~
注入到 sqlsessionfactorybean 中,集成到 mybatis 中。
使用现成的 spring-security,注入数据源后,直接查询各种权限信息。
国际化配置使用到了数据源信息。
目前就这几种,因为项目使用的是 jndi 的方式注入,所以对于多数据源来说也有一些困难。不过我的大概思路就是将数据源注入个默认数据源,使用动态 key 的方式切换数据源。
后期思路
比如,配置文件首先就需要有多个数据源的信息,如下所示:
<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>
复制代码
定义数据源路由
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();
}
}
复制代码
配置动态数据源
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.getDataSourceKey();
}
}
复制代码
集成到 SqlSessionFactoryBean,在 ReloadableSqlSessionFactoryBean 中使用动态数据源:
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")。
总的来说,将传统单数据源架构迁移到多数据源架构并不简单,但通过合理的模块梳理和逐步推进,整个过程可以得到有效实施。
文章转载自:努力的小雨
原文链接:https://www.cnblogs.com/guoxiaoyu/p/18676914
体验地址:http://www.jnpfsoft.com/?from=001YH
评论