分享

Spring Boot集成Druid实现多数据源的两种方式

 观审美2 2024-04-15 发布于北京

目录

项目场景:

一、集成com.baomidou的方式

1、maven依赖:

2、 配置文件:

3、 使用注解切换数据源: 

二、基于AOP手动实现多数据源原生的方式

1、maven依赖:

2、 配置文件:

3、 多数据源名称类

4、自定义注解 

5、配置类

6、动态数据源配置

 7、AOP切面拦截注解

8、Dao层,跟正常一样的

 9、service层,加上多数据源注解

 10、Controller测试

三、结果展示


项目场景:

Spring Boot集成Druid实现多数据源的两种方式:

1、集成com.baomidou,引入dynamic-datasource依赖;

2、原生的方式,基于AOP手动实现多数据源


一、集成com.baomidou的方式

1、maven依赖:

  1. <dependency>
  2.     <groupId>com.baomidou</groupId>
  3.     <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
  4.     <version>3.5.1</version>
  5. </dependency>
  6. <dependency>
  7.     <groupId>com.alibaba</groupId>
  8.     <artifactId>druid-spring-boot-starter</artifactId>
  9.     <version>1.1.10</version>
  10. </dependency>

2、 配置文件:

1.spring.datasource.dynamic前缀,是baomidou源码里固定的;

2.first、second是自定义的名称,可以更改;

  1. # 多数据源配置,默认master
  2. spring.datasource.dynamic.primary = first
  3. # 数据源1
  4. spring.datasource.dynamic.datasource.first.driverClassName = com.mysql.cj.jdbc.Driver
  5. spring.datasource.dynamic.datasource.first.type = com.alibaba.druid.pool.DruidDataSource
  6. spring.datasource.dynamic.datasource.first.url = jdbc:mysql://127.0.0.1:3306/mp_dm?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8
  7. spring.datasource.dynamic.datasource.first.username = test
  8. spring.datasource.dynamic.datasource.first.password = test1
  9. # 数据源2
  10. spring.datasource.dynamic.datasource.second.driverClassName = com.mysql.cj.jdbc.Driver
  11. spring.datasource.dynamic.datasource.second.type = com.alibaba.druid.pool.DruidDataSource
  12. spring.datasource.dynamic.datasource.second.url = jdbc:mysql://127.0.0.1:18306/mp_dm?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8
  13. spring.datasource.dynamic.datasource.second.username = test
  14. spring.datasource.dynamic.datasource.second.password = test1
  15. # druid多数据源全局配置
  16. spring.datasource.dynamic.druid.filter.stat.log-slow-sql = true
  17. spring.datasource.dynamic.druid.filter.stat.slow-sql-millis = 1000
  18. spring.datasource.dynamic.druid.min-evictable-idle-time-millis = 300000
  19. spring.datasource.dynamic.druid.test-on-borrow = false
  20. spring.datasource.dynamic.druid.filter.stat.merge-sql = false
  21. spring.datasource.dynamic.druid.test-on-return = false
  22. spring.datasource.dynamic.druid.initial-size = 10
  23. spring.datasource.dynamic.druid.min-idle = 10
  24. spring.datasource.dynamic.druid.max-wait = 60000
  25. spring.datasource.dynamic.druid.pool-prepared-statements = true
  26. spring.datasource.dynamic.druid.test-while-idle = true
  27. spring.datasource.dynamic.druid.validation-query = select 1
  28. spring.datasource.dynamic.druid.filter.wall.config.multi-statement-allow = true
  29. spring.datasource.dynamic.druid.time-between-eviction-runs-millis = 60000
  30. spring.datasource.dynamic.druid.max-pool-prepared-statement-per-connection-size = 20
  31. spring.datasource.dynamic.druid.max-active = 100
  32. # druid监控全局配置
  33. spring.datasource.druid.stat-view-servlet.enabled = true
  34. spring.datasource.druid.stat-view-servlet.url-pattern = /druid/*

验证配置文件是否生效:Spring Boot集成Druid查看配置是否生效_涛哥是个大帅比的博客-CSDN博客 

3、 使用注解切换数据源: 

@DS注解说明:

1.注解在方法上、类上、接口、枚举,同时存在就近原则,方法上注解优先于类上注解;

2.不使用@DS注解,默认主数据源;

类上:

  1. @DS("db2")
  2. public class test(){}

 方法上:

  1. @DS("db2")
  2. public void test(){}

mapper方法上

  1. @Select("SELECT * FROM TEST WHERE A = #{A}")
  2. @DS("db2")
  3. Map<String, Object> test(@Param("A") String A);

多数据源可能会碰到事务问题:
 Spring Boot多数据源事务@DSTransactional的使用_涛哥是个大帅比的博客-CSDN博客


二、基于AOP手动实现多数据源原生的方式

1、maven依赖:

  1. <dependency>
  2.     <groupId>com.alibaba</groupId>
  3.     <artifactId>druid-spring-boot-starter</artifactId>
  4.     <version>1.1.10</version>
  5. </dependency>

2、 配置文件:

1.Spring Boot 2.X 版本不再支持配置继承,多数据源的话每个数据源的所有配置都需要单独配置,否则配置不会生效;

2.first、second是自定义的名称,对应DynamicDataSourceConfig配置类,可以更改;

  1. # 多数据源配置
  2. spring.datasource.type = com.alibaba.druid.pool.DruidDataSource
  3. # 数据源1
  4. spring.datasource.druid.first.name = first
  5. spring.datasource.druid.first.driverClassName = com.mysql.cj.jdbc.Driver
  6. spring.datasource.druid.first.url = jdbc:mysql://127.0.0.1:3306/mp_dm?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8
  7. spring.datasource.druid.first.username = test
  8. spring.datasource.druid.first.password = test1
  9. # 数据源2
  10. spring.datasource.druid.second.name = second
  11. spring.datasource.druid.second.driverClassName = com.mysql.cj.jdbc.Driver
  12. spring.datasource.druid.second.url = jdbc:mysql://127.0.0.1:18306/mp_dm?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8
  13. spring.datasource.druid.second.username = test
  14. spring.datasource.druid.second.password = test1
  15. # druid数据源1
  16. spring.datasource.druid.first.filter.stat.log-slow-sql = true
  17. spring.datasource.druid.first.filter.stat.slow-sql-millis = 1000
  18. spring.datasource.druid.first.min-evictable-idle-time-millis = 300000
  19. spring.datasource.druid.test-on-borrow = false
  20. spring.datasource.druid.first.filter.stat.merge-sql = false
  21. spring.datasource.druid.test-on-return = false
  22. spring.datasource.druid.first.initial-size = 10
  23. spring.datasource.druid.first.min-idle = 10
  24. spring.datasource.druid.first.max-wait = 60000
  25. spring.datasource.druid.first.pool-prepared-statements = true
  26. spring.datasource.druid.first.test-while-idle = true
  27. spring.datasource.druid.first.validation-query = select 1
  28. spring.datasource.druid.first.filter.wall.config.multi-statement-allow = true
  29. spring.datasource.druid.first.time-between-eviction-runs-millis = 60000
  30. spring.datasource.druid.first.max-pool-prepared-statement-per-connection-size = 20
  31. spring.datasource.druid.first.max-active = 100
  32. # druid数据源2
  33. spring.datasource.druid.second.filter.stat.log-slow-sql = true
  34. spring.datasource.druid.second.filter.stat.slow-sql-millis = 1000
  35. spring.datasource.druid.second.min-evictable-idle-time-millis = 300000
  36. spring.datasource.druid.second.test-on-borrow = false
  37. spring.datasource.druid.second.filter.stat.merge-sql = false
  38. spring.datasource.druid.second.test-on-return = false
  39. spring.datasource.druid.second.initial-size = 10
  40. spring.datasource.druid.second.min-idle = 10
  41. spring.datasource.druid.second.max-wait = 60000
  42. spring.datasource.druid.second.pool-prepared-statements = true
  43. spring.datasource.druid.second.test-while-idle = true
  44. spring.datasource.druid.second.validation-query = select 1
  45. spring.datasource.druid.second.filter.wall.config.multi-statement-allow = true
  46. spring.datasource.druid.second.time-between-eviction-runs-millis = 60000
  47. spring.datasource.druid.second.max-pool-prepared-statement-per-connection-size = 20
  48. spring.datasource.druid.second.max-active = 100
  49. # druid监控全局配置
  50. spring.datasource.druid.stat-view-servlet.enabled = true
  51. spring.datasource.druid.stat-view-servlet.url-pattern = /druid/*

验证配置文件是否生效:Spring Boot集成Druid查看配置是否生效_涛哥是个大帅比的博客-CSDN博客 

3、 多数据源名称类

  1. package com.test.datasources;
  2. /**
  3. * 增加多数据源,在此配置
  4. */
  5. public interface DataSourceNames {
  6. String FIRST = "first";
  7. String SECOND = "second";
  8. }

4、自定义注解 

  1. package com.test.datasources.annotation;
  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. /**
  8. * 多数据源注解
  9. */
  10. //同时支持方法注解和类注解
  11. @Target({ElementType.METHOD, ElementType.TYPE})
  12. @Retention(RetentionPolicy.RUNTIME)
  13. @Documented
  14. public @interface DataSource {
  15. String value() default "";
  16. }

5、配置类

  1. package com.test.datasources;
  2. import java.util.HashMap;
  3. import java.util.Map;
  4. import javax.sql.DataSource;
  5. import org.springframework.boot.context.properties.ConfigurationProperties;
  6. import org.springframework.context.annotation.Bean;
  7. import org.springframework.context.annotation.Configuration;
  8. import org.springframework.context.annotation.Primary;
  9. import org.springframework.jdbc.core.JdbcTemplate;
  10. import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
  11. /**
  12. * 配置多数据源
  13. */
  14. @Configuration
  15. public class DynamicDataSourceConfig {
  16. @Bean
  17. @ConfigurationProperties("spring.datasource.druid.first")
  18. public DataSource firstDataSource(){
  19. return DruidDataSourceBuilder.create().build();
  20. }
  21. @Bean
  22. @ConfigurationProperties("spring.datasource.druid.second")
  23. public DataSource secondDataSource(){
  24. return DruidDataSourceBuilder.create().build();
  25. }
  26. @Bean
  27. @Primary
  28. public DynamicDataSource dataSource(DataSource firstDataSource, DataSource secondDataSource) {
  29. Map<Object, Object> targetDataSources = new HashMap<>();
  30. targetDataSources.put(DataSourceNames.FIRST, firstDataSource);
  31. targetDataSources.put(DataSourceNames.SECOND, secondDataSource);
  32. return new DynamicDataSource(firstDataSource, targetDataSources);
  33. }
  34. @Bean
  35. public JdbcTemplate jdbcTemplate() {
  36. return new JdbcTemplate(firstDataSource());
  37. }
  38. }

6、动态数据源配置

  1. package com.test.datasources;
  2. import java.util.Map;
  3. import javax.sql.DataSource;
  4. import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
  5. /**
  6. * 动态数据源,关键代码
  7. */
  8. public class DynamicDataSource extends AbstractRoutingDataSource {
  9. private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
  10. public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) {
  11. super.setDefaultTargetDataSource(defaultTargetDataSource);
  12. super.setTargetDataSources(targetDataSources);
  13. super.afterPropertiesSet();
  14. }
  15. @Override
  16. protected Object determineCurrentLookupKey() {
  17. return getDataSource();
  18. }
  19. public static void setDataSource(String dataSource) {
  20. contextHolder.set(dataSource);
  21. }
  22. public static String getDataSource() {
  23. return contextHolder.get();
  24. }
  25. public static void clearDataSource() {
  26. contextHolder.remove();
  27. }
  28. }

 7、AOP切面拦截注解

  1. package com.test.datasources.aspect;
  2. import java.lang.reflect.Method;
  3. import org.aspectj.lang.ProceedingJoinPoint;
  4. import org.aspectj.lang.annotation.Around;
  5. import org.aspectj.lang.annotation.Aspect;
  6. import org.aspectj.lang.annotation.Pointcut;
  7. import org.aspectj.lang.reflect.MethodSignature;
  8. import org.slf4j.Logger;
  9. import org.slf4j.LoggerFactory;
  10. import org.springframework.core.Ordered;
  11. import org.springframework.stereotype.Component;
  12. import com.test.datasources.DataSourceNames;
  13. import com.test.datasources.DynamicDataSource;
  14. import com.test.datasources.annotation.DataSource;
  15. /**
  16. * 多数据源,切面处理类
  17. */
  18. @Aspect
  19. @Component
  20. public class DataSourceAspect implements Ordered {
  21. protected Logger logger = LoggerFactory.getLogger(getClass());
  22. /*
  23. * @annotation匹配指定注解的方法
  24. * @within匹配指定注解的类
  25. * 注意:这里只拦截所注解的类,如果调用的是父类的方法,那么不会拦截,除非父类方法在子类中被覆盖。
  26. */
  27. @Pointcut("@annotation(com.test.datasources.annotation.DataSource) || @within(com.test.datasources.annotation.DataSource)")
  28. public void dataSourcePointCut() {
  29. }
  30. @Around("dataSourcePointCut()")
  31. public Object around(ProceedingJoinPoint point) throws Throwable {
  32. MethodSignature signature = (MethodSignature) point.getSignature();
  33. Method method = signature.getMethod();
  34. Class<?> dataClass = Class.forName(signature.getDeclaringTypeName());
  35. DataSource dsMethod = method.getAnnotation(DataSource.class);
  36. DataSource dsClass = dataClass.getAnnotation(DataSource.class);
  37. if(dsMethod != null){
  38. //方法优先,如果方法上存在注解,则优先使用方法上的注解
  39. DynamicDataSource.setDataSource(dsMethod.value());
  40. logger.debug("set datasource is " + dsMethod.value());
  41. }else if(dsClass != null){
  42. //其次类优先,如果类上存在注解,则使用类上的注解
  43. DynamicDataSource.setDataSource(dsClass.value());
  44. logger.debug("set datasource is " + dsClass.value());
  45. }else{
  46. //如果都不存在,则使用默认
  47. DynamicDataSource.setDataSource(DataSourceNames.FIRST);
  48. logger.debug("set datasource is " + DataSourceNames.FIRST);
  49. }
  50. try {
  51. return point.proceed();
  52. } finally {
  53. DynamicDataSource.clearDataSource();
  54. logger.debug("clean datasource");
  55. }
  56. }
  57. @Override
  58. public int getOrder() {
  59. return 1;
  60. }
  61. }

8、Dao层,跟正常一样的

  1. package com.test.mapper;
  2. import org.apache.ibatis.annotations.Mapper;
  3. import org.apache.ibatis.annotations.Param;
  4. import org.apache.ibatis.annotations.Select;
  5. import com.test.entity.Test1Entity;
  6. @Mapper
  7. public interface Test1Dao {
  8. @Select("select * from test1")
  9. Test1Entity getById(@Param("id")Integer id);
  10. }
  1. package com.test.mapper;
  2. import org.apache.ibatis.annotations.Mapper;
  3. import org.apache.ibatis.annotations.Param;
  4. import org.apache.ibatis.annotations.Select;
  5. import com.test.entity.Test2Entity;
  6. @Mapper
  7. public interface Test2Dao {
  8. @Select("select * from test2")
  9. Test2Entity getById(@Param("id")Integer id);
  10. }

 9、service层,加上多数据源注解

  1. package com.test.service;
  2. import org.springframework.beans.factory.annotation.Autowired;
  3. import org.springframework.stereotype.Service;
  4. import com.test.datasources.DataSourceNames;
  5. import com.test.datasources.annotation.DataSource;
  6. import com.test.entity.Test1Entity;
  7. import com.test.mapper.Test1Dao;
  8. @Service
  9. @DataSource(DataSourceNames.FIRST)
  10. public class Test1Service {
  11. @Autowired
  12. private Test1Dao test1Dao;
  13. public Test1Entity getById(int id) {
  14. return test1Dao.getById(id);
  15. }
  16. }
  1. package com.test.service;
  2. import org.springframework.beans.factory.annotation.Autowired;
  3. import org.springframework.stereotype.Service;
  4. import com.test.datasources.DataSourceNames;
  5. import com.test.datasources.annotation.DataSource;
  6. import com.test.entity.Test2Entity;
  7. import com.test.mapper.Test2Dao;
  8. @Service
  9. @DataSource(DataSourceNames.SECOND)
  10. public class Test2Service {
  11. @Autowired
  12. private Test2Dao test2Dao;
  13. public Test2Entity getById(int id) {
  14. return test2Dao.getById(id);
  15. }
  16. }

 10、Controller测试

  1. package com.test.controller;
  2. import org.springframework.beans.factory.annotation.Autowired;
  3. import org.springframework.web.bind.annotation.RequestMapping;
  4. import org.springframework.web.bind.annotation.RequestMethod;
  5. import org.springframework.web.bind.annotation.RestController;
  6. import com.test.entity.Test1Entity;
  7. import com.test.entity.Test2Entity;
  8. import com.test.service.Test1Service;
  9. import com.test.service.Test2Service;
  10. import io.swagger.annotations.Api;
  11. import io.swagger.annotations.ApiOperation;
  12. /**
  13. * 动态数据源测试
  14. */
  15. @Api(tags = "动态数据源测试")
  16. @RestController
  17. @RequestMapping("/test")
  18. public class DynamicController {
  19. @Autowired
  20. private Test1Service test1Service;
  21. @Autowired
  22. private Test2Service test2Service;
  23. @ApiOperation(value = "数据源1")
  24. @RequestMapping(value = "/test1", method = RequestMethod.GET)
  25. public String test1() {
  26. Test1Entity entity = test1Service.getById(1);
  27. return entity.getName();
  28. }
  29. @ApiOperation(value = "数据源2")
  30. @RequestMapping(value = "/test2", method = RequestMethod.GET)
  31. public String test2() {
  32. Test2Entity entity = test2Service.getById(2);
  33. return entity.getName();
  34. }
  35. }

多数据源可能会碰到事务问题:https://blog.csdn.net/u011974797/article/details/130154340

三、结果展示

服务启动会打印日志:

  1. com.alibaba.druid.pool.DruidDataSource : {dataSource-1,first} inited
  2. com.alibaba.druid.pool.DruidDataSource : {dataSource-2,second} inited

druid监控页面:

 

源码地址:https://download.csdn.net/download/u011974797/87680942

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多