Spring MVC非Hibernate动态数据源实现:MySQL多企业库登录自动连库
Spring MVC多企业动态数据源实现方案(无Hibernate适配)

核心实现基础
直接基于Spring原生提供的AbstractRoutingDataSource机制实现动态数据源路由,不需要额外引入三方组件,完全兼容无Hibernate的技术栈(可搭配JdbcTemplate/MyBatis使用)。
整体逻辑适配现有场景:1个公共库(存用户账号、企业与数据库映射关系)、5个企业专属业务库,用户登录后自动路由到对应企业的业务库。
分步落地流程
1. 全量数据源初始化
- 系统启动阶段优先加载公共库对应的数据源实例,保证公共数据可正常访问
- 从公共库读取全部5个企业对应的数据库连接配置(同实例下只需区分数据库名,可复用统一MySQL账号或单独为每个企业库配置独立账号),逐个创建独立的连接池数据源实例
- 将所有企业数据源作为可路由目标注册到自定义的动态路由数据源类中,设置公共库为默认兜底数据源:无有效登录标识时,所有请求自动走公共库连接
2. 自定义动态路由数据源类
- 新建
TenantRoutingDataSource类,直接继承AbstractRoutingDataSource,重写determineCurrentLookupKey()方法 - 该方法的返回值将作为路由查找的key,直接从线程上下文的ThreadLocal中获取当前请求对应的数据源标识,完全符合Spring数据源的查找逻辑,对上层的JdbcTemplate/MyBatis无侵入
public class TenantRoutingDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { // 从线程上下文拿当前要路由的数据源key return DataSourceContextHolder.getDataSourceKey(); } }
3. 线程上下文工具类
- 编写
DataSourceContextHolder工具类,基于ThreadLocal维护当前请求线程绑定的数据源标识 - 工具类内置三个核心方法:设置当前数据源key、读取当前数据源key、请求结束后清理上下文
必须实现上下文清理逻辑:Tomcat等容器默认使用线程池复用请求线程,如果不主动清理ThreadLocal值,会导致线程复用时出现跨用户、跨企业的数据源串连问题
public class DataSourceContextHolder { private static final ThreadLocal<String> DS_KEY_CONTEXT = new ThreadLocal<>(); public static void set(String key) { DS_KEY_CONTEXT.set(key); } public static String get() { return DS_KEY_CONTEXT.get(); } public static void clear() { DS_KEY_CONTEXT.remove(); } }
4. 登录流程的数据源绑定
- 登录请求发起时,默认走公共库数据源,从公共库校验用户ID、密码的合法性
- 校验通过后,读取该用户所属企业对应的数据源标识(如企业绑定的库ID/数据库名),将该标识与用户登录态一同存入Session
- 后续同一会话的请求,即可通过Session拿到对应的路由标识
5. 全局请求拦截器的自动路由
- 编写Spring MVC拦截器,拦截所有业务请求
- 请求进入Controller前:从Session中读取当前登录用户的数据源标识,写入
DataSourceContextHolder的ThreadLocal中 - 请求处理完成返回响应时:无论业务成功或异常,都主动调用
DataSourceContextHolder.clear()清理线程上下文,避免线程复用串数据
- 请求进入Controller前:从Session中读取当前登录用户的数据源标识,写入
- 对不需要登录的公共接口,不设置数据源标识,路由逻辑会自动走默认的公共库数据源
无Hibernate场景的适配说明
- 因为路由逻辑完全嵌入Spring数据源层面,不管上层用原生JdbcTemplate还是MyBatis作为持久层框架,只要将
TenantRoutingDataSource注入到持久层组件的数据源配置中,即可正常工作,不需要在ORM层写额外的拦截逻辑 - 核心配置示例(以XML配置为例):
<!-- 自定义动态数据源 --> <bean id="dataSource" class="com.xxx.config.TenantRoutingDataSource"> <!-- 默认兜底数据源:公共库 --> <property name="defaultTargetDataSource" ref="publicMysqlDataSource"/> <!-- 所有可切换的企业数据源 --> <property name="targetDataSources"> <map> <entry key="ent_001" value-ref="ent1MysqlDs"/> <entry key="ent_002" value-ref="ent2MysqlDs"/> <entry key="ent_003" value-ref="ent3MysqlDs"/> <entry key="ent_004" value-ref="ent4MysqlDs"/> <entry key="ent_005" value-ref="ent5MysqlDs"/> </map> </property> </bean> <!-- 给JdbcTemplate或MyBatis的SqlSessionFactory注入上面的动态数据源即可 -->
内容的提问来源于stack exchange,提问作者user15648726
相关产品推荐
相关产品推荐

