基于注解的Spring多数据源配置和使用(非事务)
1。创建DynamicDataSource类,继承AbstractRoutingDataSource
package com.rps.dataSource; import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DynamicDataSourceHolder.getDataSource(); } }
创建DynamicDataSourceHolder类
package com.rps.dataSource; public class DynamicDataSourceHolder { /** * 注意:数据源标识保存在线程变量中,避免多线程操作数据源时互相干扰 */ private static final ThreadLocal<String> THREAD_DATA_SOURCE = new ThreadLocal<String>(); public static String getDataSource() { return THREAD_DATA_SOURCE.get(); } public static void setDataSource(String dataSource) { THREAD_DATA_SOURCE.set(dataSource); } public static void clearDataSource() { THREAD_DATA_SOURCE.remove(); } }
2.配置多数据源
<util:properties id="jdbc" location="classpath:etc/mybatis/db.properties" /> <!-- 连接池配置开始 --> <!-- Druid连接池 --> <bean id="druidDataSourceAccount" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close" lazy-init="true"> <property name="driverClassName" value="#{jdbc.driverClassName}" /> <property name="url" value="#{jdbc.account_url}" /> <property name="username" value="#{jdbc.username}" /> <property name="password" value="#{jdbc.password}" /> </bean> <bean id="druidDataSourceCommon" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close" lazy-init="true"> <property name="driverClassName" value="#{jdbc.driverClassName}" /> <property name="url" value="#{jdbc.common_url}" /> <property name="username" value="#{jdbc.username}" /> <property name="password" value="#{jdbc.password}" /> </bean> <bean id="druidDataSourceData" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close" lazy-init="true"> <property name="driverClassName" value="#{jdbc.driverClassName}" /> <property name="url" value="#{jdbc.data_url}" /> <property name="username" value="#{jdbc.username}" /> <property name="password" value="#{jdbc.password}" /> </bean> <!-- 连接池配置结束 --> <!-- MyBatis整合开始 --> <bean id="dynamicDataSource" class="com.rps.dataSource.DynamicDataSource"> <property name="targetDataSources"> <map key-type="java.lang.String"> <entry key="account" value-ref="druidDataSourceAccount"></entry> <entry key="common" value-ref="druidDataSourceCommon"></entry> <entry key="data" value-ref="druidDataSourceData"></entry> </map> </property> </bean> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dynamicDataSource"/> <property name="mapperLocations" value="classpath:com/rps/**/*.xml"/> <property name="configLocation" value="classpath:etc/mybatis/mybatis-config.xml"/> </bean> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.rps" /> <property name="annotationClass" value="com.rps.annotations.MyBatisRepository" /> </bean> <!-- MyBatis整合结束 --> <!-- 配置数据库事务开始 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dynamicDataSource"/> </bean> <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true" /> <!-- 配置数据库事务结束 -->
3.在使用数据源前,选择数据源:
DynamicDataSourceHolder.setDataSource("account");
或:使用spring aop 动态切换:
package com.rps.aspect; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.stereotype.Component; import com.rps.annotations.DataSource; import com.rps.dataSource.DynamicDataSourceHolder; @Component @Aspect public class DataSourceAspect { /** * 拦截目标方法,获取由@DataSource指定的数据源标识,设置到线程存储中以便切换数据源 * * @param point * @throws Exception */ @Before("execution(* com.rps.*.model.dao.*.*(..))") public void intercept(JoinPoint point) throws Exception { System.out.println("*****************************"); Class<?> target = point.getTarget().getClass(); MethodSignature signature = (MethodSignature) point.getSignature(); // 默认使用目标类型的注解,如果没有则使用其实现接口的注解 for (Class<?> clazz : target.getInterfaces()) { resolveDataSource(clazz, signature.getMethod()); } resolveDataSource(target, signature.getMethod()); } /** * 提取目标对象方法注解和类型注解中的数据源标识 * * @param clazz * @param method */ private void resolveDataSource(Class<?> clazz, Method method) { try { Class<?>[] types = method.getParameterTypes(); // 默认使用类型注解 if (clazz.isAnnotationPresent((Class<? extends Annotation>) DataSource.class)) { DataSource source = clazz.getAnnotation(DataSource.class); DynamicDataSourceHolder.setDataSource(source.value()); } // 方法注解可以覆盖类型注解 Method m = clazz.getMethod(method.getName(), types); if (m != null && m.isAnnotationPresent(DataSource.class)) { DataSource source = m.getAnnotation(DataSource.class); DynamicDataSourceHolder.setDataSource(source.value()); } } catch (Exception e) { System.out.println(clazz + ":" + e.getMessage()); } } }
注:事务管理配置一定要配置在往DynamicDataSourceHolder 中注入数据源key之前 ,否则会报 Could not open JDBC Connection for transaction; nested exception is java.lang.IllegalStateException: Cannot determine target DataSource for lookup key [null] 找不到数据源错误
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律