Spring Boot集成Druid实现多数据源的两种方式
目录
项目场景:
一、集成com.baomidou的方式
1、maven依赖:
2、 配置文件:
二、基于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依赖:
<dependency><groupId>com.baomidou</groupId><artifactId>dynamic-datasource-spring-boot-starter</artifactId><version>3.5.1</version>
</dependency><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.1.10</version>
</dependency>
2、 配置文件:
1.spring.datasource.dynamic前缀,是baomidou源码里固定的;
2.first、second是自定义的名称,可以更改;
# 多数据源配置,默认master
spring.datasource.dynamic.primary = first
# 数据源1
spring.datasource.dynamic.datasource.first.driverClassName = com.mysql.cj.jdbc.Driver
spring.datasource.dynamic.datasource.first.type = com.alibaba.druid.pool.DruidDataSource
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
spring.datasource.dynamic.datasource.first.username = test
spring.datasource.dynamic.datasource.first.password = test1
# 数据源2
spring.datasource.dynamic.datasource.second.driverClassName = com.mysql.cj.jdbc.Driver
spring.datasource.dynamic.datasource.second.type = com.alibaba.druid.pool.DruidDataSource
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
spring.datasource.dynamic.datasource.second.username = test
spring.datasource.dynamic.datasource.second.password = test1# druid多数据源全局配置
spring.datasource.dynamic.druid.filter.stat.log-slow-sql = true
spring.datasource.dynamic.druid.filter.stat.slow-sql-millis = 1000
spring.datasource.dynamic.druid.min-evictable-idle-time-millis = 300000
spring.datasource.dynamic.druid.test-on-borrow = false
spring.datasource.dynamic.druid.filter.stat.merge-sql = false
spring.datasource.dynamic.druid.test-on-return = false
spring.datasource.dynamic.druid.initial-size = 10
spring.datasource.dynamic.druid.min-idle = 10
spring.datasource.dynamic.druid.max-wait = 60000
spring.datasource.dynamic.druid.pool-prepared-statements = true
spring.datasource.dynamic.druid.test-while-idle = true
spring.datasource.dynamic.druid.validation-query = select 1
spring.datasource.dynamic.druid.filter.wall.config.multi-statement-allow = true
spring.datasource.dynamic.druid.time-between-eviction-runs-millis = 60000
spring.datasource.dynamic.druid.max-pool-prepared-statement-per-connection-size = 20
spring.datasource.dynamic.druid.max-active = 100
# druid监控全局配置
spring.datasource.druid.stat-view-servlet.enabled = true
spring.datasource.druid.stat-view-servlet.url-pattern = /druid/*
验证配置文件是否生效:Spring Boot集成Druid查看配置是否生效_涛哥是个大帅比的博客-CSDN博客
3、 使用注解切换数据源:
@DS注解说明:
1.注解在方法上、类上、接口、枚举,同时存在就近原则,方法上注解优先于类上注解;
2.不使用@DS注解,默认主数据源;
类上:
@DS("db2")
public class test(){}
方法上:
@DS("db2")
public void test(){}
mapper方法上
@Select("SELECT * FROM TEST WHERE A = #{A}")
@DS("db2")
Map<String, Object> test(@Param("A") String A);
二、基于AOP手动实现多数据源原生的方式
1、maven依赖:
<dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.1.10</version>
</dependency>
2、 配置文件:
1.Spring Boot 2.X 版本不再支持配置继承,多数据源的话每个数据源的所有配置都需要单独配置,否则配置不会生效;
2.first、second是自定义的名称,对应DynamicDataSourceConfig配置类,可以更改;
# 多数据源配置
spring.datasource.type = com.alibaba.druid.pool.DruidDataSource
# 数据源1
spring.datasource.druid.first.name = first
spring.datasource.druid.first.driverClassName = com.mysql.cj.jdbc.Driver
spring.datasource.druid.first.url = jdbc:mysql://127.0.0.1:3306/mp_dm?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8
spring.datasource.druid.first.username = test
spring.datasource.druid.first.password = test1
# 数据源2
spring.datasource.druid.second.name = second
spring.datasource.druid.second.driverClassName = com.mysql.cj.jdbc.Driver
spring.datasource.druid.second.url = jdbc:mysql://127.0.0.1:18306/mp_dm?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8
spring.datasource.druid.second.username = test
spring.datasource.druid.second.password = test1# druid数据源1
spring.datasource.druid.first.filter.stat.log-slow-sql = true
spring.datasource.druid.first.filter.stat.slow-sql-millis = 1000
spring.datasource.druid.first.min-evictable-idle-time-millis = 300000
spring.datasource.druid.test-on-borrow = false
spring.datasource.druid.first.filter.stat.merge-sql = false
spring.datasource.druid.test-on-return = false
spring.datasource.druid.first.initial-size = 10
spring.datasource.druid.first.min-idle = 10
spring.datasource.druid.first.max-wait = 60000
spring.datasource.druid.first.pool-prepared-statements = true
spring.datasource.druid.first.test-while-idle = true
spring.datasource.druid.first.validation-query = select 1
spring.datasource.druid.first.filter.wall.config.multi-statement-allow = true
spring.datasource.druid.first.time-between-eviction-runs-millis = 60000
spring.datasource.druid.first.max-pool-prepared-statement-per-connection-size = 20
spring.datasource.druid.first.max-active = 100
# druid数据源2
spring.datasource.druid.second.filter.stat.log-slow-sql = true
spring.datasource.druid.second.filter.stat.slow-sql-millis = 1000
spring.datasource.druid.second.min-evictable-idle-time-millis = 300000
spring.datasource.druid.second.test-on-borrow = false
spring.datasource.druid.second.filter.stat.merge-sql = false
spring.datasource.druid.second.test-on-return = false
spring.datasource.druid.second.initial-size = 10
spring.datasource.druid.second.min-idle = 10
spring.datasource.druid.second.max-wait = 60000
spring.datasource.druid.second.pool-prepared-statements = true
spring.datasource.druid.second.test-while-idle = true
spring.datasource.druid.second.validation-query = select 1
spring.datasource.druid.second.filter.wall.config.multi-statement-allow = true
spring.datasource.druid.second.time-between-eviction-runs-millis = 60000
spring.datasource.druid.second.max-pool-prepared-statement-per-connection-size = 20
spring.datasource.druid.second.max-active = 100
# druid监控全局配置
spring.datasource.druid.stat-view-servlet.enabled = true
spring.datasource.druid.stat-view-servlet.url-pattern = /druid/*
验证配置文件是否生效:Spring Boot集成Druid查看配置是否生效_涛哥是个大帅比的博客-CSDN博客
3、 多数据源名称类
package com.test.datasources;/* 增加多数据源,在此配置*/
public interface DataSourceNames {String FIRST = "first";String SECOND = "second";}
4、自定义注解
package com.test.datasources.annotation;import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/* 多数据源注解*/
//同时支持方法注解和类注解
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {String value() default "";
}
5、配置类
package com.test.datasources;import java.util.HashMap;
import java.util.Map;import javax.sql.DataSource;import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.core.JdbcTemplate;import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;/* 配置多数据源*/
@Configuration
public class DynamicDataSourceConfig {@Bean@ConfigurationProperties("spring.datasource.druid.first")public DataSource firstDataSource(){return DruidDataSourceBuilder.create().build();}@Bean@ConfigurationProperties("spring.datasource.druid.second")public DataSource secondDataSource(){return DruidDataSourceBuilder.create().build();}@Bean@Primarypublic DynamicDataSource dataSource(DataSource firstDataSource, DataSource secondDataSource) {Map<Object, Object> targetDataSources = new HashMap<>();targetDataSources.put(DataSourceNames.FIRST, firstDataSource);targetDataSources.put(DataSourceNames.SECOND, secondDataSource);return new DynamicDataSource(firstDataSource, targetDataSources);}@Beanpublic JdbcTemplate jdbcTemplate() {return new JdbcTemplate(firstDataSource());}
}
6、动态数据源配置
package com.test.datasources;import java.util.Map;import javax.sql.DataSource;import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;/* 动态数据源,关键代码*/
public class DynamicDataSource extends AbstractRoutingDataSource {private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) {super.setDefaultTargetDataSource(defaultTargetDataSource);super.setTargetDataSources(targetDataSources);super.afterPropertiesSet();}@Overrideprotected Object determineCurrentLookupKey() {return getDataSource();}public static void setDataSource(String dataSource) {contextHolder.set(dataSource);}public static String getDataSource() {return contextHolder.get();}public static void clearDataSource() {contextHolder.remove();}}
7、AOP切面拦截注解
package com.test.datasources.aspect;import java.lang.reflect.Method;import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;import com.test.datasources.DataSourceNames;
import com.test.datasources.DynamicDataSource;
import com.test.datasources.annotation.DataSource;/* 多数据源,切面处理类*/
@Aspect
@Component
public class DataSourceAspect implements Ordered {protected Logger logger = LoggerFactory.getLogger(getClass());/ @annotation匹配指定注解的方法* @within匹配指定注解的类* 注意:这里只拦截所注解的类,如果调用的是父类的方法,那么不会拦截,除非父类方法在子类中被覆盖。*/@Pointcut("@annotation(com.test.datasources.annotation.DataSource) || @within(com.test.datasources.annotation.DataSource)")public void dataSourcePointCut() {}@Around("dataSourcePointCut()")public Object around(ProceedingJoinPoint point) throws Throwable {MethodSignature signature = (MethodSignature) point.getSignature();Method method = signature.getMethod();Class<?> dataClass = Class.forName(signature.getDeclaringTypeName());DataSource dsMethod = method.getAnnotation(DataSource.class);DataSource dsClass = dataClass.getAnnotation(DataSource.class);if(dsMethod != null){//方法优先,如果方法上存在注解,则优先使用方法上的注解DynamicDataSource.setDataSource(dsMethod.value());logger.debug("set datasource is " + dsMethod.value());}else if(dsClass != null){//其次类优先,如果类上存在注解,则使用类上的注解DynamicDataSource.setDataSource(dsClass.value());logger.debug("set datasource is " + dsClass.value());}else{//如果都不存在,则使用默认DynamicDataSource.setDataSource(DataSourceNames.FIRST);logger.debug("set datasource is " + DataSourceNames.FIRST);}try {return point.proceed();} finally {DynamicDataSource.clearDataSource();logger.debug("clean datasource");}}@Overridepublic int getOrder() {return 1;}
}
8、Dao层,跟正常一样的
package com.test.mapper;import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;import com.test.entity.Test1Entity;@Mapper
public interface Test1Dao {@Select("select * from test1")Test1Entity getById(@Param("id")Integer id);
}
package com.test.mapper;import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;import com.test.entity.Test2Entity;@Mapper
public interface Test2Dao {@Select("select * from test2")Test2Entity getById(@Param("id")Integer id);
}
9、service层,加上多数据源注解
package com.test.service;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import com.test.datasources.DataSourceNames;
import com.test.datasources.annotation.DataSource;
import com.test.entity.Test1Entity;
import com.test.mapper.Test1Dao;@Service
@DataSource(DataSourceNames.FIRST)
public class Test1Service {@Autowiredprivate Test1Dao test1Dao;public Test1Entity getById(int id) {return test1Dao.getById(id);}
}
package com.test.service;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import com.test.datasources.DataSourceNames;
import com.test.datasources.annotation.DataSource;
import com.test.entity.Test2Entity;
import com.test.mapper.Test2Dao;@Service
@DataSource(DataSourceNames.SECOND)
public class Test2Service {@Autowiredprivate Test2Dao test2Dao;public Test2Entity getById(int id) {return test2Dao.getById(id);}
}
10、Controller测试
package com.test.controller;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;import com.test.entity.Test1Entity;
import com.test.entity.Test2Entity;
import com.test.service.Test1Service;
import com.test.service.Test2Service;import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;/* 动态数据源测试*/
@Api(tags = "动态数据源测试")
@RestController
@RequestMapping("/test")
public class DynamicController {@Autowiredprivate Test1Service test1Service;@Autowiredprivate Test2Service test2Service;@ApiOperation(value = "数据源1")@RequestMapping(value = "/test1", method = RequestMethod.GET)public String test1() {Test1Entity entity = test1Service.getById(1);return entity.getName();}@ApiOperation(value = "数据源2")@RequestMapping(value = "/test2", method = RequestMethod.GET)public String test2() {Test2Entity entity = test2Service.getById(2);return entity.getName();}
}
三、结果展示
服务启动会打印日志:
com.alibaba.druid.pool.DruidDataSource : {dataSource-1,first} inited
com.alibaba.druid.pool.DruidDataSource : {dataSource-2,second} inited
druid监控页面:
源码地址:https://download.csdn.net/download/u011974797/87680942