多數據源,很多項目都用得到,比較實用。在 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