经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » Java相关 » Spring » 查看文章
springboot配置多个数据源两种方式实现
来源:jb51  时间:2022/3/7 16:37:32  对本文有异议

在我们的实际业务中可能会遇到;在一个项目里面读取多个数据库的数据来进行展示,spring对同时配置多个数据源是支持的。

本文中将展示两种方法来实现这个功能。

springboot+mybatis

第一种方式:

在配置文件中配置多个数据源,然后通过配置类来获取数据源以及mapper相关的扫描配置

pom.xml

  1. <parent>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-parent</artifactId>
  4. <version>1.5.9.RELEASE</version>
  5. <relativePath/>
  6. </parent>
  7. <dependencies>
  8. <!-- druid数据源驱动 -->
  9. <dependency>
  10. <groupId>com.alibaba</groupId>
  11. <artifactId>druid-spring-boot-starter</artifactId>
  12. <version>1.1.0</version>
  13. </dependency>
  14. <dependency>
  15. <groupId>mysql</groupId>
  16. <artifactId>mysql-connector-java</artifactId>
  17. </dependency>
  18. <!--mybatis SpringBoot依赖 -->
  19. <dependency>
  20. <groupId>org.springframework.boot</groupId>
  21. <artifactId>spring-boot-starter-web</artifactId>
  22. </dependency>
  23. <dependency>
  24. <groupId>org.springframework.boot</groupId>
  25. <artifactId>spring-boot-starter-tomcat</artifactId>
  26. <scope>compile</scope>
  27. </dependency>
  28. <dependency>
  29. <groupId>org.springframework.boot</groupId>
  30. <artifactId>spring-boot-starter-test</artifactId>
  31. </dependency>
  32. <!-- aop依赖 -->
  33. <dependency>
  34. <groupId>org.springframework.boot</groupId>
  35. <artifactId>spring-boot-starter-aop</artifactId>
  36. </dependency>
  37. <!-- mybatis -->
  38. <dependency>
  39. <groupId>org.mybatis.spring.boot</groupId>
  40. <artifactId>mybatis-spring-boot-starter</artifactId>
  41. <version>1.3.1</version>
  42. </dependency>
  43. <!-- 通用mapper -->
  44. <dependency>
  45. <groupId>tk.mybatis</groupId>
  46. <artifactId>mapper-spring-boot-starter</artifactId>
  47. <version>1.1.5</version>
  48. </dependency>
  49. <!-- druid监控依赖 -->
  50. <dependency>
  51. <groupId>com.alibaba</groupId>
  52. <artifactId>druid</artifactId>
  53. <version>1.0.28</version>
  54. </dependency>
  55. </dependencies>

application.yml

  1. spring:
  2. datasource:
  3. type: com.alibaba.druid.pool.DruidDataSource
  4. driverClassName: com.mysql.jdbc.Driver
  5. url: jdbc:mysql://127.0.0.1:3306/test2?useUnicode=true&characterEncoding=utf8
  6. username: root
  7. password: 123456
  8. url2: jdbc:mysql://127.0.0.1:3306/test1?useUnicode=true&characterEncoding=utf8
  9. username2: root
  10. password2: 123456

 DruidDBConfig 连接池相关配置

  1. package com.xbz.common.config;
  2. import com.alibaba.druid.support.http.StatViewServlet;
  3. import com.alibaba.druid.support.http.WebStatFilter;
  4. import org.slf4j.Logger;
  5. import org.slf4j.LoggerFactory;
  6. import org.springframework.boot.web.servlet.FilterRegistrationBean;
  7. import org.springframework.boot.web.servlet.ServletRegistrationBean;
  8. import org.springframework.context.annotation.Bean;
  9. import org.springframework.context.annotation.Configuration;
  10. /**
  11. * Druid监控
  12. */
  13. @SuppressWarnings("AlibabaRemoveCommentedCode")
  14. @Configuration
  15. public class DruidDBConfig {
  16. private Logger logger = LoggerFactory.getLogger(DruidDBConfig.class);
  17. /**
  18. * 注册ServletRegistrationBean
  19. * @return
  20. */
  21. @Bean
  22. public ServletRegistrationBean druidServlet() {
  23. ServletRegistrationBean reg = new ServletRegistrationBean();
  24. reg.setServlet(new StatViewServlet());
  25. reg.addUrlMappings("/druid/*");
  26. reg.addInitParameter("allow", ""); //白名单
  27. return reg;
  28. }
  29. /**
  30. * 注册FilterRegistrationBean
  31. * @return
  32. */
  33. @Bean
  34. public FilterRegistrationBean filterRegistrationBean() {
  35. FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
  36. filterRegistrationBean.setFilter(new WebStatFilter());
  37. filterRegistrationBean.addUrlPatterns("/*");
  38. filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
  39. filterRegistrationBean.addInitParameter("profileEnable", "true");
  40. filterRegistrationBean.addInitParameter("principalCookieName", "USER_COOKIE");
  41. filterRegistrationBean.addInitParameter("principalSessionName", "USER_SESSION");
  42. filterRegistrationBean.addInitParameter("DruidWebStatFilter", "/*");
  43. return filterRegistrationBean;
  44. }
  45. }

MasterDbConfig 注意读取数据库连接相关的键,以及扫描实体、mapper等

  1. package com.xbz.common.config;
  2. import com.alibaba.druid.pool.DruidDataSource;
  3. import org.apache.ibatis.session.SqlSessionFactory;
  4. import org.mybatis.spring.SqlSessionFactoryBean;
  5. import org.mybatis.spring.annotation.MapperScan;
  6. import org.slf4j.Logger;
  7. import org.slf4j.LoggerFactory;
  8. import org.springframework.beans.factory.annotation.Qualifier;
  9. import org.springframework.beans.factory.annotation.Value;
  10. import org.springframework.context.annotation.Bean;
  11. import org.springframework.context.annotation.Configuration;
  12. import org.springframework.context.annotation.Primary;
  13. import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
  14. import org.springframework.jdbc.datasource.DataSourceTransactionManager;
  15. import javax.sql.DataSource;
  16. import java.sql.SQLException;
  17. @Configuration
  18. @MapperScan(basePackages = MasterDbConfig.PACKAGE , sqlSessionFactoryRef = "masterSqlSessionFactory")
  19. public class MasterDbConfig {
  20. private Logger logger = LoggerFactory.getLogger(MasterDbConfig.class);
  21. // 精确到 master 目录,以便跟其他数据源隔离
  22. static final String PACKAGE = "com.xbz.**.dao.master";
  23. private static final String MAPPER_LOCATION = "classpath*:mapper/master/*.xml";
  24. private static final String DOMAIN_PACKAGE = "com.xbz.**.domain";
  25. @Value("${spring.datasource.url}")
  26. private String dbUrl;
  27. @Value("${spring.datasource.username}")
  28. private String username;
  29. @Value("${spring.datasource.password}")
  30. private String password;
  31. @Value("${spring.datasource.driverClassName}")
  32. private String driverClassName;
  33. @Bean(name="masterDataSource") //声明其为Bean实例
  34. @Primary //在同样的DataSource中,首先使用被标注的DataSource
  35. public DataSource masterDataSource() {
  36. DruidDataSource datasource = new DruidDataSource();
  37. datasource.setUrl(this.dbUrl);
  38. datasource.setUsername(username);
  39. datasource.setPassword(password);
  40. datasource.setDriverClassName(driverClassName);
  41. return datasource;
  42. }
  43. @Bean(name = "masterTransactionManager")
  44. @Primary
  45. public DataSourceTransactionManager masterTransactionManager() {
  46. return new DataSourceTransactionManager(masterDataSource());
  47. }
  48. @Bean(name = "masterSqlSessionFactory")
  49. @Primary
  50. public SqlSessionFactory masterSqlSessionFactory(@Qualifier("masterDataSource") DataSource masterDataSource)
  51. throws Exception {
  52. final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
  53. sessionFactory.setDataSource(masterDataSource);
  54. sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver()
  55. .getResources(MasterDbConfig.MAPPER_LOCATION));
  56. sessionFactory.setTypeAliasesPackage(DOMAIN_PACKAGE);
  57. //mybatis 数据库字段与实体类属性驼峰映射配置
  58. sessionFactory.getObject().getConfiguration().setMapUnderscoreToCamelCase(true);
  59. return sessionFactory.getObject();
  60. }
  61. }

ClusterDbConfig 

  1. package com.xbz.common.config;
  2. import com.alibaba.druid.pool.DruidDataSource;
  3. import org.apache.ibatis.session.SqlSessionFactory;
  4. import org.mybatis.spring.SqlSessionFactoryBean;
  5. import org.mybatis.spring.annotation.MapperScan;
  6. import org.slf4j.Logger;
  7. import org.slf4j.LoggerFactory;
  8. import org.springframework.beans.factory.annotation.Qualifier;
  9. import org.springframework.beans.factory.annotation.Value;
  10. import org.springframework.context.annotation.Bean;
  11. import org.springframework.context.annotation.Configuration;
  12. import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
  13. import org.springframework.jdbc.datasource.DataSourceTransactionManager;
  14. import javax.sql.DataSource;
  15. import java.sql.SQLException;
  16. /**
  17. * 从数据源配置
  18. * 若需要配置更多数据源 , 直接在yml中添加数据源配置再增加相应的新的数据源配置类即可
  19. */
  20. @Configuration
  21. @MapperScan(basePackages = ClusterDbConfig.PACKAGE , sqlSessionFactoryRef = "clusterSqlSessionFactory")
  22. public class ClusterDbConfig {
  23. private Logger logger = LoggerFactory.getLogger(ClusterDbConfig.class);
  24. // 精确到 cluster 目录,以便跟其他数据源隔离
  25. static final String PACKAGE = "com.xbz.**.dao.cluster";
  26. private static final String MAPPER_LOCATION = "classpath*:mapper/cluster/*.xml";
  27. private static final String DOMAIN_PACKAGE = "com.xbz.**.domain";
  28. @Value("${spring.datasource.url2}")
  29. private String dbUrl;
  30. @Value("${spring.datasource.username2}")
  31. private String username;
  32. @Value("${spring.datasource.password2}")
  33. private String password;
  34. @Value("${spring.datasource.driverClassName}")
  35. private String driverClassName;
  36. @Bean(name="clusterDataSource") //声明其为Bean实例
  37. public DataSource clusterDataSource() {
  38. DruidDataSource datasource = new DruidDataSource();
  39. datasource.setUrl(this.dbUrl);
  40. datasource.setUsername(username);
  41. datasource.setPassword(password);
  42. datasource.setDriverClassName(driverClassName);
  43. return datasource;
  44. }
  45. @Bean(name = "clusterTransactionManager")
  46. public DataSourceTransactionManager clusterTransactionManager() {
  47. return new DataSourceTransactionManager(clusterDataSource());
  48. }
  49. @Bean(name = "clusterSqlSessionFactory")
  50. public SqlSessionFactory clusterSqlSessionFactory(@Qualifier("clusterDataSource") DataSource culsterDataSource)
  51. throws Exception {
  52. final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
  53. sessionFactory.setDataSource(culsterDataSource);
  54. sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver()
  55. .getResources(ClusterDbConfig.MAPPER_LOCATION));
  56. sessionFactory.setTypeAliasesPackage(DOMAIN_PACKAGE);
  57. //mybatis 数据库字段与实体类属性驼峰映射配置
  58. sessionFactory.getObject().getConfiguration().setMapUnderscoreToCamelCase(true);
  59. return sessionFactory.getObject();
  60. }
  61. }

不同的数据源配置不佟的mapper扫描位置,然后需要哪一个数据源就注入哪一个mapper接口即可

这样获取的数据就是来自不同的数据源了,这种方法比较简单。

方法二

配置一个默认使用的数据源,然后定义多个其他的数据源,使用aop形成注解式选择数据源

  1. # 默认数据源
  2. spring:
  3. datasource:
  4. druid:
  5. # 数据库访问配置, 使用druid数据源
  6. type: com.alibaba.druid.pool.DruidDataSource
  7. driverClassName: com.mysql.jdbc.Driver
  8. url: jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf8
  9. username: root
  10. password: 123456
  11. # 其他数据源
  12. custom:
  13. datasource:
  14. druid:
  15. type: com.alibaba.druid.pool.DruidDataSource
  16. names: fishlog,fishgame
  17. fishlog:
  18. driverClassName: com.mysql.jdbc.Driver
  19. url: jdbc:mysql://127.0.0.1:3306/test1?useUnicode=true&characterEncoding=utf8
  20. username: root
  21. password: 123456
  22. fishgame:
  23. driverClassName: com.mysql.jdbc.Driver
  24. url: jdbc:mysql://127.0.0.1:3306/test2?useUnicode=true&characterEncoding=utf8
  25. username: root
  26. password: 123456

然后启动类中注入注解

注解类及参数value TargetDataSource 

  1. package com.sysmg.common.config.datasoure;
  2. import java.lang.annotation.Documented;
  3. import java.lang.annotation.ElementType;
  4. import java.lang.annotation.Retention;
  5. import java.lang.annotation.RetentionPolicy;
  6. import java.lang.annotation.Target;
  7. @Target({ ElementType.METHOD, ElementType.TYPE })
  8. @Retention(RetentionPolicy.RUNTIME)
  9. @Documented
  10. public @interface TargetDataSource {
  11. String value();
  12. }

 DynamicDataSource 

  1. package com.sysmg.common.config.datasoure;
  2. import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
  3. public class DynamicDataSource extends AbstractRoutingDataSource {
  4. @Override
  5. protected Object determineCurrentLookupKey() {
  6. return DynamicDataSourceContextHolder.getDataSourceType();
  7. }
  8. }

DynamicDataSourceAspect切面实现

  1. package com.sysmg.common.config.datasoure;
  2. import org.aspectj.lang.JoinPoint;
  3. import org.aspectj.lang.annotation.After;
  4. import org.aspectj.lang.annotation.Aspect;
  5. import org.aspectj.lang.annotation.Before;
  6. import org.springframework.core.annotation.Order;
  7. import org.springframework.stereotype.Component;
  8. @Aspect
  9. @Order(-10) // 保证该AOP在@Transactional之前执行
  10. @Component
  11. public class DynamicDataSourceAspect {
  12. @Before("@annotation(targetDataSource)")
  13. public void changeDataSource(JoinPoint point, TargetDataSource targetDataSource) throws Throwable {
  14. String dsId = targetDataSource.value();
  15. if (!DynamicDataSourceContextHolder.containsDataSource(dsId)) {
  16. System.err.println("数据源[{}]不存在,使用默认数据源 > {}" + targetDataSource.value() + point.getSignature());
  17. } else {
  18. System.out.println("UseDataSource : {} > {}" + targetDataSource.value() + point.getSignature());
  19. DynamicDataSourceContextHolder.setDataSourceType(targetDataSource.value());
  20. }
  21. }
  22. @After("@annotation(targetDataSource)")
  23. public void restoreDataSource(JoinPoint point, TargetDataSource targetDataSource) {
  24. System.out.println("RevertDataSource : {} > {}"+targetDataSource.value()+point.getSignature());
  25. DynamicDataSourceContextHolder.clearDataSourceType();
  26. }
  27. }

DynamicDataSourceContextHolder

  1. package com.sysmg.common.config.datasoure;
  2. import java.util.ArrayList;
  3. import java.util.List;
  4. public class DynamicDataSourceContextHolder {
  5. private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();
  6. public static List<String> dataSourceIds = new ArrayList<String>();
  7. public static void setDataSourceType(String dataSourceType) {
  8. contextHolder.set(dataSourceType);
  9. }
  10. public static String getDataSourceType() {
  11. return contextHolder.get();
  12. }
  13. public static void clearDataSourceType() {
  14. contextHolder.remove();
  15. }
  16. public static boolean containsDataSource(String dataSourceId){
  17. return dataSourceIds.contains(dataSourceId);
  18. }
  19. }

DynamicDataSourceRegister数据源注册类

  1. package com.sysmg.common.config.datasoure;
  2. import java.util.HashMap;
  3. import java.util.Map;
  4. import javax.sql.DataSource;
  5. import org.springframework.beans.MutablePropertyValues;
  6. import org.springframework.beans.PropertyValues;
  7. import org.springframework.beans.factory.support.BeanDefinitionRegistry;
  8. import org.springframework.beans.factory.support.GenericBeanDefinition;
  9. import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
  10. import org.springframework.boot.bind.RelaxedDataBinder;
  11. import org.springframework.boot.bind.RelaxedPropertyResolver;
  12. import org.springframework.context.EnvironmentAware;
  13. import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
  14. import org.springframework.core.convert.ConversionService;
  15. import org.springframework.core.convert.support.DefaultConversionService;
  16. import org.springframework.core.env.Environment;
  17. import org.springframework.core.type.AnnotationMetadata;
  18. public class DynamicDataSourceRegister implements ImportBeanDefinitionRegistrar, EnvironmentAware {
  19. private static final Object DATASOURCE_TYPE_DEFAULT = "com.alibaba.druid.pool.DruidDataSource";
  20. private ConversionService conversionService = new DefaultConversionService();
  21. private PropertyValues dataSourcePropertyValues;
  22. private DataSource defaultDataSource;
  23. private Map<String, DataSource> customDataSources = new HashMap<String, DataSource>();
  24. @Override
  25. public void setEnvironment(Environment environment) {
  26. System.out.println("DynamicDataSourceRegister.setEnvironment()");
  27. initDefaultDataSource(environment);
  28. initCustomDataSources(environment);
  29. }
  30. private void initDefaultDataSource(Environment env) {
  31. RelaxedPropertyResolver propertyResolver = new RelaxedPropertyResolver(env, "spring.datasource.druid.");
  32. Map<String, Object> dsMap = new HashMap<String, Object>();
  33. dsMap.put("type", propertyResolver.getProperty("type"));
  34. dsMap.put("driverClassName", propertyResolver.getProperty("driverClassName"));
  35. dsMap.put("url", propertyResolver.getProperty("url"));
  36. dsMap.put("username", propertyResolver.getProperty("username"));
  37. dsMap.put("password", propertyResolver.getProperty("password"));
  38. defaultDataSource = buildDataSource(dsMap);
  39. dataBinder(defaultDataSource, env);
  40. }
  41. private void initCustomDataSources(Environment env) {
  42. RelaxedPropertyResolver propertyResolver = new RelaxedPropertyResolver(env, "custom.datasource.druid.");
  43. String dsPrefixs = propertyResolver.getProperty("names");
  44. for (String dsPrefix : dsPrefixs.split(",")) {// 多个数据源
  45. Map<String, Object> dsMap = propertyResolver.getSubProperties(dsPrefix + ".");
  46. DataSource ds = buildDataSource(dsMap);
  47. customDataSources.put(dsPrefix, ds);
  48. dataBinder(ds, env);
  49. }
  50. }
  51. @SuppressWarnings("unchecked")
  52. public DataSource buildDataSource(Map<String, Object> dsMap) {
  53. Object type = dsMap.get("type");
  54. if (type == null) {
  55. type = DATASOURCE_TYPE_DEFAULT;// 默认DataSource
  56. }
  57. Class<? extends DataSource> dataSourceType;
  58. try {
  59. dataSourceType = (Class<? extends DataSource>) Class.forName((String) type);
  60. String driverClassName = dsMap.get("driverClassName").toString();
  61. String url = dsMap.get("url").toString();
  62. String username = dsMap.get("username").toString();
  63. String password = dsMap.get("password").toString();
  64. DataSourceBuilder factory = DataSourceBuilder.create().driverClassName(driverClassName).url(url)
  65. .username(username).password(password).type(dataSourceType);
  66. return factory.build();
  67. } catch (ClassNotFoundException e) {
  68. e.printStackTrace();
  69. }
  70. return null;
  71. }
  72. private void dataBinder(DataSource dataSource, Environment env) {
  73. RelaxedDataBinder dataBinder = new RelaxedDataBinder(dataSource);
  74. dataBinder.setConversionService(conversionService);
  75. dataBinder.setIgnoreNestedProperties(false);// false
  76. dataBinder.setIgnoreInvalidFields(false);// false
  77. dataBinder.setIgnoreUnknownFields(true);// true
  78. if (dataSourcePropertyValues == null) {
  79. Map<String, Object> rpr = new RelaxedPropertyResolver(env, "spring.datasource").getSubProperties(".");
  80. Map<String, Object> values = new HashMap<>(rpr);
  81. values.remove("type");
  82. values.remove("driverClassName");
  83. values.remove("url");
  84. values.remove("username");
  85. values.remove("password");
  86. dataSourcePropertyValues = new MutablePropertyValues(values);
  87. }
  88. dataBinder.bind(dataSourcePropertyValues);
  89. }
  90. @Override
  91. public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
  92. System.out.println("DynamicDataSourceRegister.registerBeanDefinitions()");
  93. Map<Object, Object> targetDataSources = new HashMap<Object, Object>();
  94. targetDataSources.put("dataSource", defaultDataSource);
  95. DynamicDataSourceContextHolder.dataSourceIds.add("dataSource");
  96. targetDataSources.putAll(customDataSources);
  97. for (String key : customDataSources.keySet()) {
  98. DynamicDataSourceContextHolder.dataSourceIds.add(key);
  99. }
  100. GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
  101. beanDefinition.setBeanClass(DynamicDataSource.class);
  102. beanDefinition.setSynthetic(true);
  103. MutablePropertyValues mpv = beanDefinition.getPropertyValues();
  104. mpv.addPropertyValue("defaultTargetDataSource", defaultDataSource);
  105. mpv.addPropertyValue("targetDataSources", targetDataSources);
  106. registry.registerBeanDefinition("dataSource", beanDefinition);
  107. }
  108. }

这里使用的时候把需要使用的表名注入,如果是默认的数据源,则不需要加这个注解,具体的springboot自定义注解的建立,可参考https://www.jb51.net/article/239952.htm,这里的mapper文件和实体扫描可以跟默认的数据源配置相同即可

到此这篇关于springboot配置多个数据源两种方式实现的文章就介绍到这了,更多相关springboot配置多个数据源内容请搜索w3xue以前的文章或继续浏览下面的相关文章希望大家以后多多支持w3xue!

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号