多数据源,很多项目都用得到,比较实用。在 DEMO: springboot 与 freemarker 集成 基础上进行修改。
修改后的项目结构大致这样:
1、src/main/resources/application.properties 中配置好多个数据源

datasource.jdbc.driverClassName = com.mysql.jdbc.Driver
datasource.jdbc.url = jdbc:mysql://127.0.0.1:3306/test
datasource.jdbc.username = root
datasource.jdbc.password = test
# function
datasource.jdbc.url_stg = jdbc:mysql://10.199.xx.35:3306/test
datasource.jdbc.username_stg = root
datasource.jdbc.password_stg = test
# mybatis #
mybatis.typeAliasesPackage=demo.springboot.model
mybatis.mapperLocations=classpath:/mybatis/mapper/*.xml
2、定义一个枚举类型 DatabaseType.java,表示不同的环境

package demo.springboot.enums; /** * @author meng.geng * function 功能环境 * regression 回归环境 * * */ public enum DatabaseType { function("function", "1"), regression("regression", "2"); DatabaseType(String name, String value){ this.name = name; this.value = value; } private String name; private String value; public String getName(){ return name; } public String getValue(){ return value; } }
3、定义一个 DatabaseContextHolder.java, 保存一个线程安全的DatabaseType容器

package demo.springboot.conf; import demo.springboot.enums.DatabaseType; /** * @author danny.yao * 作用:保存一个线程安全的DatabaseType容器 * **/ public class DatabaseContextHolder { private static final ThreadLocal<DatabaseType> contextHolder = new ThreadLocal<>(); public static void setDatabaseType(DatabaseType type){ contextHolder.set(type); } public static DatabaseType getDatabaseType(){ return contextHolder.get(); } }
4、定义动态数据源获取的方法 DynamicDataSource.java,集成 AbstractRoutingDataSource

package demo.springboot.conf; import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; /** * @author danny.yao * 动态数据源(需要继承AbstractRoutingDataSource) * 作用:使用DatabaseContextHolder获取当前线程的DatabaseType * * */ public class DynamicDataSource extends AbstractRoutingDataSource { protected Object determineCurrentLookupKey() { return DatabaseContextHolder.getDatabaseType(); } }
5、mybatis多数据源的配置 MybatisConfig.java

package demo.springboot.conf; import java.util.HashMap; import java.util.Map; import javax.sql.DataSource; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.annotation.MapperScan; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.core.env.Environment; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import com.alibaba.druid.pool.DruidDataSource; import demo.springboot.enums.DatabaseType; /** * * @author danny.yao * springboot集成mybatis基本入口 * 1、创建数据源 * 2、创建SqlSessionFactory */ @Configuration @MapperScan(basePackages="demo.springboot.mapper", sqlSessionFactoryRef="sessionFactory") public class MybatisConfig { @Autowired Environment environment; @Value("${datasource.jdbc.driverClassName}") private String dbDriver; @Value("${datasource.jdbc.url}") private String dbUrl; @Value("${datasource.jdbc.username}") private String dbUsername; @Value("${datasource.jdbc.password}") private String dbPassword; @Value("${datasource.jdbc.url_stg}") private String dbUrl_stg; @Value("${datasource.jdbc.username_stg}") private String dbUsername_stg; @Value("${datasource.jdbc.password_stg}") private String dbPassword_stg; /** * 创建 local环境 dataSource * @throws Exception */ @Bean(name="dataSourceFunctional") public DataSource dataSourceLocal() throws Exception{ DruidDataSource dataSource = new DruidDataSource(); dataSource.setDriverClassName(dbDriver); dataSource.setUrl(dbUrl); dataSource.setUsername(dbUsername); dataSource.setPassword(dbPassword); return dataSource; } /** * 创建 回归环境 dataSource * @throws Exception */ @Bean(name="dataSourceRegression") public DataSource dataSourceStaging() throws Exception{ DruidDataSource dataSource = new DruidDataSource(); dataSource.setDriverClassName(dbDriver); dataSource.setUrl(dbUrl_stg); dataSource.setUsername(dbUsername_stg); dataSource.setPassword(dbPassword_stg); return dataSource; } /** * 1、创建动态数据源 * @throws Exception * @Primary该注解表示在同一个接口有多个类可以注入的时候,默认选择哪个,而不是让@Autowired报错 */ @Bean(name="dynamicDataSource") @Primary public DynamicDataSource DataSource(@Qualifier("dataSourceFunctional") DataSource dataSourceFunctional, @Qualifier("dataSourceRegression") DataSource dataSourceRegression){ Map<Object, Object> targetDataSource = new HashMap<>(); targetDataSource.put(DatabaseType.function, dataSourceFunctional); targetDataSource.put(DatabaseType.regression, dataSourceRegression); DynamicDataSource dataSource = new DynamicDataSource(); dataSource.setTargetDataSources(targetDataSource); dataSource.setDefaultTargetDataSource(dataSourceFunctional); return dataSource; } /** * 2、根据数据源创建SqlSessionFactory * @throws Exception */ @Bean(name="sessionFactory") public SqlSessionFactory sessionFactory(@Qualifier("dynamicDataSource")DynamicDataSource dataSource) throws Exception{ SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean(); sessionFactoryBean.setDataSource(dataSource); PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); sessionFactoryBean.setMapperLocations(resolver.getResources(environment.getProperty("mybatis.mapperLocations"))); //*Mapper.xml位置 return sessionFactoryBean.getObject(); } }
6、修改服务类 StudentService.java:添加环境切换方法setDataSourceByEnvironment,同时在获取数据的方法中切换数据源

package demo.springboot.service; import java.util.ArrayList; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import demo.springboot.conf.DatabaseContextHolder; import demo.springboot.enums.DatabaseType; import demo.springboot.mapper.StudentMapper; import demo.springboot.model.Student; @Service public class StudentService { @Autowired private StudentMapper studentMapper; public void setDataSourceByEnvironment(String environment){ if (environment.equals(DatabaseType.function.getValue())){ DatabaseContextHolder.setDatabaseType(DatabaseType.function); } if (environment.equals(DatabaseType.regression.getValue())){ DatabaseContextHolder.setDatabaseType(DatabaseType.regression); } } public List<Student> listStudents(String environment){ setDataSourceByEnvironment(environment); List<Student> students = new ArrayList<>(); students = studentMapper.listStudents(); return students; } }
7、修改controller类 StudentController.java : 请求需要带上环境参数 @RequestParam(value="environment", required=true)String environment

package demo.springboot.controller; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import demo.springboot.model.Student; import demo.springboot.service.StudentService; @Controller @RequestMapping("/student") public class StudentController { private static final Logger LOGGER = LoggerFactory.getLogger(StudentController.class); @Autowired private StudentService studentService; @RequestMapping("/list") public String listStudent(@RequestParam(value="environment", required=true)String environment, Map<String, Object> map){ List<Student> students = new ArrayList<>(); students = studentService.listStudents(environment); for (Student student : students){ LOGGER.info(student.getId() + " - " + student.getName() + " - " + student.getNo() + " - " + student.getSex()); } map.put("students", students); return "student"; } }
8、测试
1)请求环境1的数据:http://localhost:8080/student/list?environment=1
2)请求环境2的数据:http://localhost:8080/student/list?environment=2
---
开心工作,认真生活;回望来时路,脚印三两,笑声无数...
开心工作,认真生活;回望来时路,脚印三两,笑声无数...
标签:
springboot
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?