当前位置:   article > 正文

Spring boot 多数据源配置(分配置文件和数据库表两种情况)_springboot多数据源配置保存在主数据库

springboot多数据源配置保存在主数据库
由于项目有很多数据源需要配置,所以参考网上的资料研究了下。现在有2种实现方案,一种是数据源信息在项目中不会很多就2-3个那种,那就直接读取配置文件就行了,还有一种是有很多数据源的那种成百上千的可以考虑写到数据表中。
一、配置在配置文件中使用方法
1、配置文件application.properties( 需要注意所有数据源共享主数据源的其它属性配置,如果需要不同的数据源配置不同的数据源配置,需要修改代码)
# 数据库访问配置
# 主数据源,默认的
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.url= jdbc:mysql://127.0.0.1:3306/test0?useUnicode=true&characterEncoding=UTF-8
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=root1234

# 更多数据源
com.pgl.edi.datasource.names=test1,test2 

com.pgl.edi.datasource.test1.type=com.alibaba.druid.pool.DruidDataSource
com.pgl.edi.datasource.test1.driver-class-name=com.mysql.jdbc.Driver
com.pgl.edi.datasource.test1.url=jdbc:mysql://127.0.0.1:3306/test1?useUnicode=true&characterEncoding=UTF-8
com.pgl.edi.datasource.test1.username=root
com.pgl.edi.datasource.test1.password=root1234

com.pgl.edi.datasource.test2.type=com.alibaba.druid.pool.DruidDataSource
com.pgl.edi.datasource.test2.driver-class-name=com.mysql.jdbc.Driver
com.pgl.edi.datasource.test2.url= jdbc:mysql://127.0.0.1:3306/test2?useUnicode=true&characterEncoding=UTF-8
com.pgl.edi.datasource.test2.username=root
com.pgl.edi.datasource.test2.password=root1234
# 下面为连接池的补充设置,应用到上面所有数据源中
# 初始化大小,最小,最大
spring.datasource.initialSize=5
spring.datasource.minIdle=5
spring.datasource.maxActive=20
# 配置获取连接等待超时的时间
spring.datasource.maxWait=60000
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
spring.datasource.timeBetweenEvictionRunsMillis=60000
# 配置一个连接在池中最小生存的时间,单位是毫秒
spring.datasource.minEvictableIdleTimeMillis=300000
spring.datasource.validationQuery=SELECT 1 FROM t_user
spring.datasource.testWhileIdle=true
spring.datasource.testOnBorrow=true
spring.datasource.testOnReturn=false
# 打开PSCache,并且指定每个连接上PSCache的大小
spring.datasource.poolPreparedStatements=true
spring.datasource.maxPoolPreparedStatementPerConnectionSize=20
# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
spring.datasource.filters=stat,wall,log4j
2、然后是注册数据源类DynamicDataSourceRegister 如下
  1. import java.util.HashMap;
  2. import java.util.Map;
  3. import javax.sql.DataSource;
  4. import org.slf4j.Logger;
  5. import org.slf4j.LoggerFactory;
  6. import org.springframework.beans.MutablePropertyValues;
  7. import org.springframework.beans.PropertyValues;
  8. import org.springframework.beans.factory.support.BeanDefinitionRegistry;
  9. import org.springframework.beans.factory.support.GenericBeanDefinition;
  10. import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
  11. import org.springframework.boot.bind.RelaxedDataBinder;
  12. import org.springframework.boot.bind.RelaxedPropertyResolver;
  13. import org.springframework.context.EnvironmentAware;
  14. import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
  15. import org.springframework.core.convert.ConversionService;
  16. import org.springframework.core.convert.support.DefaultConversionService;
  17. import org.springframework.core.env.Environment;
  18. import org.springframework.core.type.AnnotationMetadata;
  19. /**
  20. *
  21. * 功能描述:动态数据源注册
  22. * 启动动态数据源请在启动类中(如Start)
  23. * 添加 @Import(DynamicDataSourceRegister.class)
  24. */
  25. public class DynamicDataSourceRegister implements ImportBeanDefinitionRegistrar, EnvironmentAware {
  26. private static final Logger logger = LoggerFactory.getLogger(DynamicDataSourceRegister.class);
  27. private ConversionService conversionService = new DefaultConversionService();
  28. private PropertyValues dataSourcePropertyValues;
  29. // 如配置文件中未指定数据源类型,使用该默认值
  30. private static final Object DATASOURCE_TYPE_DEFAULT = "org.apache.tomcat.jdbc.pool.DataSource";
  31. // 数据源
  32. private DataSource defaultDataSource;
  33. private Map<String, DataSource> customDataSources = new HashMap<>();
  34. @Override
  35. public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
  36. Map<Object, Object> targetDataSources = new HashMap<Object, Object>();
  37. // 将主数据源添加到更多数据源中
  38. targetDataSources.put("dataSource", defaultDataSource);
  39. DynamicDataSourceContextHolder.dataSourceIds.add("dataSource");
  40. // 添加更多数据源
  41. targetDataSources.putAll(customDataSources);
  42. for (String key : customDataSources.keySet()) {
  43. DynamicDataSourceContextHolder.dataSourceIds.add(key);
  44. }
  45. // 创建DynamicDataSource
  46. GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
  47. beanDefinition.setBeanClass(DynamicDataSource.class);
  48. beanDefinition.setSynthetic(true);
  49. MutablePropertyValues mpv = beanDefinition.getPropertyValues();
  50. mpv.addPropertyValue("defaultTargetDataSource", defaultDataSource);
  51. mpv.addPropertyValue("targetDataSources", targetDataSources);
  52. registry.registerBeanDefinition("dataSource", beanDefinition);
  53. logger.info("Dynamic DataSource Registry");
  54. }
  55. /**
  56. * 创建DataSource
  57. *
  58. * @param type
  59. * @param driverClassName
  60. * @param url
  61. * @param username
  62. * @param password
  63. * @return
  64. */
  65. @SuppressWarnings("unchecked")
  66. public DataSource buildDataSource(Map<String, Object> dsMap) {
  67. try {
  68. Object type = dsMap.get("type");
  69. if (type == null)
  70. type = DATASOURCE_TYPE_DEFAULT;// 默认DataSource
  71. Class<? extends DataSource> dataSourceType;
  72. dataSourceType = (Class<? extends DataSource>) Class.forName((String) type);
  73. String driverClassName = dsMap.get("driver-class-name").toString();
  74. String url = dsMap.get("url").toString();
  75. String username = dsMap.get("username").toString();
  76. String password = dsMap.get("password").toString();
  77. DataSourceBuilder factory = DataSourceBuilder.create().driverClassName(driverClassName).url(url)
  78. .username(username).password(password).type(dataSourceType);
  79. return factory.build();
  80. } catch (ClassNotFoundException e) {
  81. e.printStackTrace();
  82. }
  83. return null;
  84. }
  85. /**
  86. * 加载多数据源配置
  87. */
  88. @Override
  89. public void setEnvironment(Environment env) {
  90. initDefaultDataSource(env);
  91. initCustomDataSources(env);
  92. }
  93. /**
  94. * 初始化主数据源
  95. *
  96. */
  97. private void initDefaultDataSource(Environment env) {
  98. // 读取主数据源
  99. RelaxedPropertyResolver propertyResolver = new RelaxedPropertyResolver(env, "spring.datasource.");
  100. Map<String, Object> dsMap = new HashMap<>();
  101. dsMap.put("type", propertyResolver.getProperty("type"));
  102. dsMap.put("driver-class-name", propertyResolver.getProperty("driver-class-name"));
  103. dsMap.put("url", propertyResolver.getProperty("url"));
  104. dsMap.put("username", propertyResolver.getProperty("username"));
  105. dsMap.put("password", propertyResolver.getProperty("password"));
  106. defaultDataSource = buildDataSource(dsMap);
  107. dataBinder(defaultDataSource, env);
  108. }
  109. /**
  110. * 为DataSource绑定更多数据
  111. *
  112. * @param dataSource
  113. * @param env
  114. */
  115. private void dataBinder(DataSource dataSource, Environment env) {
  116. RelaxedDataBinder dataBinder = new RelaxedDataBinder(dataSource);
  117. // dataBinder.setValidator(new
  118. // LocalValidatorFactory().run(this.applicationContext));
  119. dataBinder.setConversionService(conversionService);
  120. dataBinder.setIgnoreNestedProperties(false);// false
  121. dataBinder.setIgnoreInvalidFields(false);// false
  122. dataBinder.setIgnoreUnknownFields(true);// true
  123. if (dataSourcePropertyValues == null) {
  124. Map<String, Object> rpr = new RelaxedPropertyResolver(env, "spring.datasource").getSubProperties(".");
  125. Map<String, Object> values = new HashMap<>(rpr);
  126. // 排除已经设置的属性
  127. values.remove("type");
  128. values.remove("driver-class-name");
  129. values.remove("url");
  130. values.remove("username");
  131. values.remove("password");
  132. dataSourcePropertyValues = new MutablePropertyValues(values);
  133. }
  134. dataBinder.bind(dataSourcePropertyValues);
  135. }
  136. /**
  137. * 初始化更多数据源
  138. *
  139. */
  140. private void initCustomDataSources(Environment env) {
  141. // 读取配置文件获取更多数据源,也可以通过defaultDataSource读取数据库获取更多数据源
  142. RelaxedPropertyResolver propertyResolver = new RelaxedPropertyResolver(env, "com.pgl.edi.datasource.");//这里写自己的配置的数据源前缀
  143. String dsPrefixs = propertyResolver.getProperty("names");//根据自己定义的获取数据源key
  144. for (String dsPrefix : dsPrefixs.split(",")) {// 多个数据源
  145. Map<String, Object> dsMap = propertyResolver.getSubProperties(dsPrefix + ".");
  146. DataSource ds = buildDataSource(dsMap);
  147. customDataSources.put(dsPrefix, ds);
  148. dataBinder(ds, env);
  149. }
  150. }
  151. }
上面是注册多数据源代码,下面是如何使用数据源需要以下几个类DynamicDataSourceContextHolder、DynamicDataSourceAspect、DynamicDataSource、DataSource。
  1. import java.util.ArrayList;
  2. import java.util.List;
  3. public class DynamicDataSourceContextHolder {
  4. private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();
  5. public static List<String> dataSourceIds = new ArrayList<>();
  6. public static void setDataSourceType(String dataSourceType) {
  7. contextHolder.set(dataSourceType);
  8. }
  9. public static String getDataSourceType() {
  10. return contextHolder.get();
  11. }
  12. public static void clearDataSourceType() {
  13. contextHolder.remove();
  14. }
  15. /**
  16. * 判断指定DataSrouce当前是否存在
  17. *
  18. * @param dataSourceId
  19. * @return
  20. */
  21. public static boolean containsDataSource(String dataSourceId) {
  22. return dataSourceIds.contains(dataSourceId);
  23. }
  24. }
  1. import org.aspectj.lang.JoinPoint;
  2. import org.aspectj.lang.annotation.After;
  3. import org.aspectj.lang.annotation.Aspect;
  4. import org.aspectj.lang.annotation.Before;
  5. import org.slf4j.Logger;
  6. import org.slf4j.LoggerFactory;
  7. import org.springframework.core.annotation.Order;
  8. import org.springframework.stereotype.Component;
  9. @Aspect
  10. @Order(-1)// 保证该AOP在@Transactional之前执行
  11. @Component
  12. public class DynamicDataSourceAspect {
  13. private static final Logger logger = LoggerFactory.getLogger(DynamicDataSourceAspect.class);
  14. @Before("@annotation(ds)")
  15. public void changeDataSource(JoinPoint point, DataSource ds) throws Throwable {
  16. String dsId = ds.name();
  17. if (!DynamicDataSourceContextHolder.containsDataSource(dsId)) {
  18. logger.error("数据源[{}]不存在,使用默认数据源 > {}", ds.name(), point.getSignature());
  19. } else {
  20. logger.debug("Use DataSource : {} > {}", ds.name(), point.getSignature());
  21. DynamicDataSourceContextHolder.setDataSourceType(ds.name());
  22. }
  23. }
  24. @After("@annotation(ds)")
  25. public void restoreDataSource(JoinPoint point, DataSource ds) {
  26. logger.debug("Revert DataSource : {} > {}", ds.name(), point.getSignature());
  27. DynamicDataSourceContextHolder.clearDataSourceType();
  28. }
  29. }
  1. import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
  2. public class DynamicDataSource extends AbstractRoutingDataSource {
  3. @Override
  4. protected Object determineCurrentLookupKey() {
  5. return DynamicDataSourceContextHolder.getDataSourceType();
  6. }
  7. }
  1. mport java.lang.annotation.Documented;
  2. import java.lang.annotation.ElementType;
  3. import java.lang.annotation.Retention;
  4. import java.lang.annotation.RetentionPolicy;
  5. import java.lang.annotation.Target;
  6. @Target({ ElementType.METHOD, ElementType.TYPE })
  7. @Retention(RetentionPolicy.RUNTIME)
  8. @Documented
  9. public @interface DataSource {
  10. String name();
  11. }
以上就是这四个类的代码。使用的时候需要注意在启动类上使用@Import(DynamicDataSourceRegister.class),在要使用方法用注解的方式@DataSource(name="test1"),这样就可以使用了。如果没有指定数据源则使用默认数据源。还有一点配置文件中的names就是我们所使用数据源的key。
二、数据源信息配置在数据库中
1、配置主数据源,主数据源主要是访问存储其他数据源信息的。
# 主数据源,默认的
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.url= jdbc:mysql://127.0.0.1:3306/test0?useUnicode=true&characterEncoding=UTF-8
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=root1234
然后建立数据库dataource表如下
  1. CREATE TABLE `datasource` (
  2. `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  3. `type` varchar(255) COLLATE utf8_bin NOT NULL COMMENT '数据源类型',
  4. `driver_class_name` varchar(255) COLLATE utf8_bin NOT NULL COMMENT '数据库驱动类',
  5. `url` varchar(255) COLLATE utf8_bin NOT NULL COMMENT '连接url',
  6. `username` varchar(255) COLLATE utf8_bin NOT NULL COMMENT '用户名',
  7. `password` varchar(255) COLLATE utf8_bin NOT NULL COMMENT '密码',
  8. `rmark` varchar(255) COLLATE utf8_bin DEFAULT NULL COMMENT '备注',
  9. `dsname` varchar(255) COLLATE utf8_bin NOT NULL COMMENT '数据源名称',
  10. `status` varchar(255) COLLATE utf8_bin NOT NULL DEFAULT '1' COMMENT '状态',
  11. PRIMARY KEY (`id`)
  12. ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
在项目配置文件配置datasource表所在数据库信息,在这里也可以配置数据源的其他信息,比如初始化大小、最大连接大小等信息。( 需要注意所有数据源共享主数据源的其它属性配置,如果需要不同的数据源配置不同的数据源配置,需要修改代码)。需要修改的地方就一个DynamicDataSourceRegister类中initCustomDataSources方法需要修改为如下
  1. private void initCustomDataSources(Environment env) {
  2. // 读取库表中datasource获取更多数据源
  3. Map<String, Map<String, Object>> customInfo=getCustomDataSourceInfo();
  4. for (String key : customInfo.keySet()) {
  5. Map<String, Object> dsMap = customInfo.get(key);
  6. DataSource ds = buildDataSource(dsMap);
  7. customDataSources.put(key, ds);
  8. dataBinder(ds, env);
  9. }
  10. }
  11. private Map<String, Map<String, Object>> getCustomDataSourceInfo() {
  12. Map<String, Map<String, Object>> customMap = new HashMap<>();
  13. String sql = "select type,`driver_class_name`,url,username,`password`,`dsname` from datasource where status=1";
  14. JdbcTemplate jdbcTemplate=new JdbcTemplate(defaultDataSource);
  15. List<DataSourceInfo> infos=jdbcTemplate.query(sql, new RowMapper<DataSourceInfo>() {
  16. @Override
  17. public DataSourceInfo mapRow(ResultSet rs, int rowNum) throws SQLException {
  18. DataSourceInfo info=new DataSourceInfo();
  19. info.setType(rs.getString("type"));
  20. info.setDriverClassName(rs.getString("driver_class_name"));
  21. info.setUrl(rs.getString("url"));
  22. info.setPassword(rs.getString("password"));
  23. info.setUsername(rs.getString("username"));
  24. info.setDsName(rs.getString("dsname"));
  25. return info;
  26. }
  27. });
  28. for(DataSourceInfo info:infos) {
  29. Map<String, Object> dsMap = new HashMap<>();
  30. dsMap.put("type", info.getType());
  31. dsMap.put("driver-class-name", info.getDriverClassName());
  32. dsMap.put("url", info.getUrl());
  33. dsMap.put("username", info.getUsername());
  34. dsMap.put("password", info.getPassword());
  35. customMap.put(info.getDsName(), dsMap);
  36. }
  37. return customMap;
  38. }
使用方式和第一种一样。
在很多时候我们可能不喜欢用注解的方式指定使用的数据源,我们可以修改DynamicDataSourceAspect类可以使用如下配置
  1. @Aspect
  2. @Order(-10) // 保证该AOP在@Transactional之前执行
  3. @Component
  4. public class DynamicDataSourceAspect {
  5. private static final Logger logger = LoggerFactory.getLogger(DynamicDataSourceAspect.class);
  6. //由于我项目中使用方法名后缀是FromDB进入AOP处理且默认第一个参数为使用数据源的key
  7. //这里可以自己修改想要以什么方式匹配使用数据源
  8. @Pointcut("execution(* com.pgl.demo.service.impl..*.*FromDB(..))")
  9. public void excude() {
  10. }
  11. @Before("excude()")
  12. public void changeDataSource(JoinPoint point) throws Throwable {
  13. Object obj = point.getArgs()[0];
  14. if(obj instanceof String) {
  15. String dsName=obj.toString();
  16. if (!DynamicDataSourceContextHolder.containsDataSource(dsName)) {
  17. logger.error("数据源[{}]不存在,使用默认数据源 > {}", obj, point.getSignature());
  18. throw new Throwable("数据源["+dsName+"]不存在");
  19. } else {
  20. logger.info("Use DataSource : {} > {}", dsName, point.getSignature());
  21. DynamicDataSourceContextHolder.setDataSourceType(dsName);
  22. }
  23. logger.info("-----------args DataSource : {} > {}", dsName, point.getSignature());
  24. }else {
  25. logger.error("数据源[{}]不存在", obj);
  26. throw new Throwable("数据源["+obj+"]不存在");
  27. }
  28. }
  29. @After("excude()")
  30. public void restoreDataSource(JoinPoint point) {
  31. Object obj = point.getArgs()[0];
  32. logger.info("Revert DataSource : {} > {}", obj.toString(), point.getSignature());
  33. DynamicDataSourceContextHolder.clearDataSourceType();
  34. }
  35. }
使用这种方式的话要拦截的方法必须不能private的。













声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/756999
推荐阅读
相关标签
  

闽ICP备14008679号