目录
项目场景:
一、集成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 dynamic-datasource-spring-boot-starter3.5.1 com.alibaba druid-spring-boot-starter1.1.10
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博客
@DS注解说明:
1.注解在方法上、类上、接口、枚举,同时存在就近原则,方法上注解优先于类上注解;
2.不使用@DS注解,默认主数据源;
类上:
@DS("db2") public class test(){}
方法上:
@DS("db2") public void test(){}
mapper方法上
@Select("SELECT * FROM TEST WHERE A = #{A}") @DS("db2") Maptest(@Param("A") String A);
多数据源可能会碰到事务问题:
Spring Boot多数据源事务@DSTransactional的使用_涛哥是个大帅比的博客-CSDN博客
com.alibaba druid-spring-boot-starter1.1.10
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博客
package com.test.datasources; /** * 增加多数据源,在此配置 */ public interface DataSourceNames { String FIRST = "first"; String SECOND = "second"; }
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 ""; }
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 @Primary public DynamicDataSource dataSource(DataSource firstDataSource, DataSource secondDataSource) { Map
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 ThreadLocalcontextHolder = new ThreadLocal<>(); public DynamicDataSource(DataSource defaultTargetDataSource, Map
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"); } } @Override public int getOrder() { return 1; } }
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); }
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 { @Autowired private 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 { @Autowired private Test2Dao test2Dao; public Test2Entity getById(int id) { return test2Dao.getById(id); } }
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 { @Autowired private Test1Service test1Service; @Autowired private 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(); } }
多数据源可能会碰到事务问题:https://blog.csdn.net/u011974797/article/details/130154340
服务启动会打印日志:
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