小言_互联网的博客

【超详细demo】Spring Boot 多数据源配置

337人阅读  评论(0)

第一种方式: AbstractRoutingDataSource

1.1. 手动切换数据源

application.properties


  
  1. # Order
  2. # 如果用Druid作为数据源,应该用url属性,而不是jdbc-url
  3. spring.datasource.order.jdbc-url= jdbc: mysql:/ /localhost:3306/order?useUnicode= true&characterEncoding=utf8&allowMultiQueries= true&useSSL= false
  4. spring.datasource.order.username=root
  5. spring.datasource.order.password= 123456
  6. spring.datasource.order.driver- class-name=com.mysql.cj.jdbc.Driver
  7. # Stock
  8. spring.datasource.stock.jdbc-url= jdbc: mysql:/ /localhost:3306/stock?useUnicode= true&characterEncoding=utf8&allowMultiQueries= true&useSSL= false
  9. spring.datasource.stock.username=root
  10. spring.datasource.stock.password= 123456
  11. spring.datasource.stock.driver- class-name=com.mysql.cj.jdbc.Driver
  12. # Account
  13. spring.datasource.account.jdbc-url= jdbc: mysql:/ /localhost:3306/account?useUnicode= true&characterEncoding=utf8&allowMultiQueries= true&useSSL= false
  14. spring.datasource.account.username=root
  15. spring.datasource.account.password= 123456
  16. spring.datasource.account.driver- class-name=com.mysql.cj.jdbc.Driver

配置数据源

DataSourceConfig.java


  
  1. package com.cjs.example.config;
  2. import com.alibaba.druid.pool.DruidDataSource;
  3. import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
  4. import com.zaxxer.hikari.HikariDataSource;
  5. import org.mybatis.spring.SqlSessionFactoryBean;
  6. import org.springframework.beans.factory. annotation.Qualifier;
  7. import org.springframework.boot.context.properties.ConfigurationProperties;
  8. import org.springframework.boot.jdbc.DataSourceBuilder;
  9. import org.springframework.context. annotation.Bean;
  10. import org.springframework.context. annotation.Configuration;
  11. import org.springframework.context. annotation.Primary;
  12. import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
  13. import javax.sql.DataSource;
  14. import java.util.HashMap;
  15. import java.util.Map;
  16. @Configuration
  17. public class DataSourceConfig {
  18. @Bean("orderDataSource")
  19. @ConfigurationProperties(prefix = "spring.datasource.order")
  20. public DataSource orderDataSource() {
  21. // return new HikariDataSource();
  22. // return new DruidDataSource();
  23. return DataSourceBuilder.create().build();
  24. }
  25. @Bean("accountDataSource")
  26. @ConfigurationProperties(prefix = "spring.datasource.account")
  27. public DataSource accountDataSource() {
  28. // return new HikariDataSource();
  29. // return new DruidDataSource();
  30. return DataSourceBuilder.create().build();
  31. }
  32. @Bean("stockDataSource")
  33. @ConfigurationProperties(prefix = "spring.datasource.stock")
  34. public DataSource stockDataSource() {
  35. // return new HikariDataSource();
  36. // return new DruidDataSource();
  37. return DataSourceBuilder.create().build();
  38. }
  39. @Primary
  40. @Bean("dynamicDataSource")
  41. public DataSource dynamicDataSource( @Qualifier("orderDataSource") DataSource orderDataSource,
  42. @Qualifier("accountDataSource") DataSource accountDataSource,
  43. @Qualifier("stockDataSource") DataSource stockDataSource) {
  44. Map<Object, Object> dataSourceMap = new HashMap<>( 3);
  45. dataSourceMap.put(DataSourceKey.ORDER.name(), orderDataSource);
  46. dataSourceMap.put(DataSourceKey.STOCK.name(), stockDataSource);
  47. dataSourceMap.put(DataSourceKey.ACCOUNT.name(), accountDataSource);
  48. DynamicRoutingDataSource dynamicRoutingDataSource = new DynamicRoutingDataSource();
  49. dynamicRoutingDataSource.setDefaultTargetDataSource(orderDataSource);
  50. dynamicRoutingDataSource.setTargetDataSources(dataSourceMap);
  51. return dynamicRoutingDataSource;
  52. }
  53. /* https://baomidou.com/pages/3b5af0/ */
  54. @Bean
  55. public MybatisSqlSessionFactoryBean sqlSessionFactoryBean( @Qualifier("dynamicDataSource") DataSource dataSource) {
  56. MybatisSqlSessionFactoryBean sqlSessionFactoryBean = new MybatisSqlSessionFactoryBean();
  57. sqlSessionFactoryBean.setDataSource(dataSource);
  58. // sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mappers/*.xml"));
  59. return sqlSessionFactoryBean;
  60. }
  61. }

由于是MyBatsi-Plus,所以配的是MybatisSqlSessionFactoryBean,如果是MyBatis,则应该是SqlSessionFactoryBean

DataSourceKey.java


  
  1. package com.cjs.example.config;
  2. public enum DataSourceKey {
  3. /**
  4. * Order data source key.
  5. */
  6. ORDER,
  7. /**
  8. * Stock data source key.
  9. */
  10. STOCK,
  11. /**
  12. * Account data source key.
  13. */
  14. ACCOUNT
  15. }

DynamicDataSourceContextHolder.java


  
  1. package com. cjs. example. config;
  2. public class DynamicDataSourceContextHolder {
  3. private static final ThreadLocal< String> CONTEXT_HOLDER = ThreadLocal. withInitial( DataSourceKey. ORDER::name);
  4. public static void setDataSourceKey( DataSourceKey key) {
  5. CONTEXT_HOLDER. set(key. name());
  6. }
  7. public static String getDataSourceKey( ) {
  8. return CONTEXT_HOLDER. get();
  9. }
  10. public static void clearDataSourceKey( ) {
  11. CONTEXT_HOLDER. remove();
  12. }
  13. }

DynamicRoutingDataSource.java


  
  1. package com.cjs.example.config;
  2. import lombok.extern.slf4j. Slf4j;
  3. import org.springframework.jdbc.datasource.lookup. AbstractRoutingDataSource;
  4. @Slf4j
  5. public class DynamicRoutingDataSource extends AbstractRoutingDataSource {
  6. @Override
  7. protected Object determineCurrentLookupKey() {
  8. log.info( "当前数据源 [{}]", DynamicDataSourceContextHolder.getDataSourceKey());
  9. return DynamicDataSourceContextHolder.getDataSourceKey();
  10. }
  11. }

好了,配置完以后,在操作数据库之前,先设置用哪个数据源即可,就像下面这样:

DynamicDataSourceContextHolder.setDataSourceKey(DataSourceKey.ACCOUNT);

举个例子:


  
  1. package com.cjs.example;
  2. import com.cjs.example.account.entity.Account;
  3. import com.cjs.example.account.service.IAccountService;
  4. import com.cjs.example.config.DataSourceKey;
  5. import com.cjs.example.config.DynamicDataSourceContextHolder;
  6. import com.cjs.example.order.entity.Order;
  7. import com.cjs.example.order.service.IOrderService;
  8. import com.cjs.example.stock.entity.Stock;
  9. import com.cjs.example.stock.service.IStockService;
  10. import org.junit.jupiter.api.Test;
  11. import org.springframework.beans.factory.annotation.Autowired;
  12. import org.springframework.boot.test.context.SpringBootTest;
  13. import java.math.BigDecimal;
  14. @SpringBootTest
  15. public class Demo1122ApplicationTests {
  16. @Autowired
  17. private IOrderService orderService;
  18. @Autowired
  19. private IAccountService accountService;
  20. @Autowired
  21. private IStockService stockService;
  22. @Test
  23. public void doBusiness () {
  24. DynamicDataSourceContextHolder.setDataSourceKey(DataSourceKey.ORDER);
  25. Order order = new Order();
  26. order.setOrderNo( "123");
  27. order.setUserId( "1");
  28. order.setCommodityCode( "abc");
  29. order.setCount( 1);
  30. order.setAmount( new BigDecimal( "9.9"));
  31. orderService.save(order);
  32. DynamicDataSourceContextHolder.setDataSourceKey(DataSourceKey.STOCK);
  33. Stock stock = new Stock();
  34. stock.setId( 1);
  35. stock.setCommodityCode( "abc");
  36. stock.setName( "huawei");
  37. stock.setCount( 1);
  38. stockService.updateById(stock);
  39. DynamicDataSourceContextHolder.setDataSourceKey(DataSourceKey.ACCOUNT);
  40. Account account = new Account();
  41. account.setId( 1);
  42. account.setUserId( "1");
  43. account.setAmount( new BigDecimal( 100));
  44. accountService.updateById(account);
  45. }
  46. }

这样写看起来确实有些麻烦,通常可能不会像这样在一个方法里操作多个数据库,就比如说假设这是一个管理后台,为了图省事把所有业务都写在这一个项目里,这个时候就需要配置多个数据源,各个数据库的业务互相没有关联,只是写在同一个项目中而已,这样的话如果每次都手动设置数据源太麻烦,可以定义一个AOP切面来自动切换数据源。

1.2. 自动切换数据源

Core Technologies

給刚才的代码升个级,利用AOP来拦截目标方法自动切换数据源

1、添加@EnableAspectJAutoProxy注解


  
  1. package com.cjs.example;
  2. import org.mybatis.spring. annotation.MapperScan;
  3. import org.springframework.boot.SpringApplication;
  4. import org.springframework.boot.autoconfigure.SpringBootApplication;
  5. import org.springframework.context. annotation.EnableAspectJAutoProxy;
  6. @EnableAspectJAutoProxy
  7. @MapperScan("com.cjs.example.*.mapper")
  8. @SpringBootApplication
  9. public class Demo1122Application {
  10. public static void main(String[] args) {
  11. SpringApplication.run(Demo1122Application. class, args);
  12. }
  13. }

2、定义切面、切点、通知


  
  1. package com.cjs.example.aop;
  2. import com.cjs.example.config.DataSourceKey;
  3. import com.cjs.example.config.DynamicDataSourceContextHolder;
  4. import org.aspectj.lang.ProceedingJoinPoint;
  5. import org.aspectj.lang.annotation.Around;
  6. import org.aspectj.lang.annotation.Aspect;
  7. import org.aspectj.lang.annotation.Pointcut;
  8. import org.springframework.stereotype.Component;
  9. @Aspect
  10. @Component
  11. public class DataSourceAdvice {
  12. // @Pointcut("within(com.cjs.example.order..*)")
  13. @Pointcut("execution(* com.cjs.example.order..*.*(..))")
  14. public void orderPointcut () {}
  15. // @Pointcut("within(com.cjs.example.account..*)")
  16. @Pointcut("execution(* com.cjs.example.account..*.*(..))")
  17. public void accountPointcut () {}
  18. // @Pointcut("within(com.cjs.example.stock..*)")
  19. @Pointcut("execution(* com.cjs.example.stock..*.*(..))")
  20. public void stockPointcut () {}
  21. @Around("orderPointcut()")
  22. public Object order (ProceedingJoinPoint pjp) throws Throwable {
  23. DynamicDataSourceContextHolder.setDataSourceKey(DataSourceKey.ORDER);
  24. Object retVal = pjp.proceed();
  25. DynamicDataSourceContextHolder.clearDataSourceKey();
  26. return retVal;
  27. }
  28. @Around("accountPointcut()")
  29. public Object account (ProceedingJoinPoint pjp) throws Throwable {
  30. DynamicDataSourceContextHolder.setDataSourceKey(DataSourceKey.ACCOUNT);
  31. Object retVal = pjp.proceed();
  32. DynamicDataSourceContextHolder.clearDataSourceKey();
  33. return retVal;
  34. }
  35. @Around("stockPointcut()")
  36. public Object stock (ProceedingJoinPoint pjp) throws Throwable {
  37. DynamicDataSourceContextHolder.setDataSourceKey(DataSourceKey.STOCK);
  38. Object retVal = pjp.proceed();
  39. DynamicDataSourceContextHolder.clearDataSourceKey();
  40. return retVal;
  41. }
  42. }

现在就不用每次调用service方法前手动设置数据源了

工程结构

第二种方式:dynamic-datasource-spring-boot-starter

功能很强大,支持 数据源分组 ,适用于多种场景 纯粹多库  读写分离  一主多从  混合模式

GitHub - baomidou/dynamic-datasource-spring-boot-starter: dynamic datasource for springboot 多数据源 动态数据源 主从分离 读写分离 分布式事务

1、引入dynamic-datasource-spring-boot-starter


  
  1. <dependency>
  2. <groupId>com.baomidou </groupId>
  3. <artifactId>dynamic-datasource-spring-boot-starter </artifactId>
  4. <version>3.5.2 </version>
  5. </dependency>

2、配置数据源


  
  1. spring:
  2. datasource:
  3. dynamic:
  4. primary: master #设置默认的数据源或者数据源组,默认值即为master
  5. strict: false #严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源
  6. datasource:
  7. master:
  8. url: jdbc: mysql:/ /xx.xx.xx.xx:3306/dynamic
  9. username: root
  10. password: 123456
  11. driver- class-name: com.mysql.jdbc.Driver # 3.2.0开始支持SPI可省略此配置
  12. slave_1:
  13. url: jdbc: mysql:/ /xx.xx.xx.xx:3307/dynamic
  14. username: root
  15. password: 123456
  16. driver- class-name: com.mysql.jdbc.Driver
  17. slave_2:
  18. url: ENC(xxxxx) # 内置加密,使用请查看详细文档
  19. username: ENC(xxxxx)
  20. password: ENC(xxxxx)
  21. driver- class-name: com.mysql.jdbc.Driver
  22. #......省略
  23. #以上会配置一个默认库master,一个组slave下有两个子库slave_1,slave_2

主从配置,读写分离


  
  1. # 多主多从 纯粹多库(记得设置 primary) 混合配置
  2. spring: spring: spring:
  3. datasource: datasource: datasource:
  4. dynamic: dynamic: dynamic:
  5. datasource: datasource: datasource:
  6. master_1: mysql: master:
  7. master_2: oracle: slave_1:
  8. slave_1: sqlserver: slave_2:
  9. slave_2: postgresql: oracle_1:
  10. slave_3: h2: oracle_2:

改造一下前面的例子


  
  1. spring.datasource.dynamic.primary=order
  2. # Order
  3. spring.datasource.dynamic.datasource.order.url= jdbc: mysql:/ /localhost:3306/order?useUnicode= true&characterEncoding=utf8&allowMultiQueries= true&useSSL= false
  4. spring.datasource.dynamic.datasource.order.username=root
  5. spring.datasource.dynamic.datasource.order.password= 123456
  6. spring.datasource.dynamic.datasource.order.driver- class-name=com.mysql.cj.jdbc.Driver
  7. # Stock
  8. spring.datasource.dynamic.datasource.stock.url= jdbc: mysql:/ /localhost:3306/stock?useUnicode= true&characterEncoding=utf8&allowMultiQueries= true&useSSL= false
  9. spring.datasource.dynamic.datasource.stock.username=root
  10. spring.datasource.dynamic.datasource.stock.password= 123456
  11. spring.datasource.dynamic.datasource.stock.driver- class-name=com.mysql.cj.jdbc.Driver
  12. # Account
  13. spring.datasource.dynamic.datasource.account.url= jdbc: mysql:/ /localhost:3306/account?useUnicode= true&characterEncoding=utf8&allowMultiQueries= true&useSSL= false
  14. spring.datasource.dynamic.datasource.account.username=root
  15. spring.datasource.dynamic.datasource.account.password= 123456
  16. spring.datasource.dynamic.datasource.account.driver- class-name=com.mysql.cj.jdbc.Driver

3、使用 @DS 切换数据源

@DS 可以注解在方法上或类上,同时存在就近原则 方法上注解 优先于 类上注解

注解 结果
没有@DS 默认数据源
@DS("dsName")   dsName可以为组名也可以为具体某个库的名称

  
  1. package com.cjs.example.order.service.impl;
  2. import com.baomidou.dynamic.datasource.annotation. DS;
  3. import com.cjs.example.order.entity. Order;
  4. import com.cjs.example.order.mapper. OrderMapper;
  5. import com.cjs.example.order.service. IOrderService;
  6. import com.baomidou.mybatisplus.extension.service.impl. ServiceImpl;
  7. import org.springframework.stereotype. Service;
  8. @DS( "order")
  9. @Service
  10. public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements IOrderService {
  11. }

  
  1. package com.cjs.example.stock.service.impl;
  2. import com.baomidou.dynamic.datasource.annotation. DS;
  3. import com.cjs.example.stock.entity. Stock;
  4. import com.cjs.example.stock.mapper. StockMapper;
  5. import com.cjs.example.stock.service. IStockService;
  6. import com.baomidou.mybatisplus.extension.service.impl. ServiceImpl;
  7. import org.springframework.stereotype. Service;
  8. @DS( "stock")
  9. @Service
  10. public class StockServiceImpl extends ServiceImpl<StockMapper, Stock> implements IStockService {
  11. }

  
  1. package com.cjs.example.account.service.impl;
  2. import com.baomidou.dynamic.datasource.annotation. DS;
  3. import com.cjs.example.account.entity. Account;
  4. import com.cjs.example.account.mapper. AccountMapper;
  5. import com.cjs.example.account.service. IAccountService;
  6. import com.baomidou.mybatisplus.extension.service.impl. ServiceImpl;
  7. import org.springframework.stereotype. Service;
  8. @DS( "account")
  9. @Service
  10. public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> implements IAccountService {
  11. }


转载:https://blog.csdn.net/wufaqidong1/article/details/127990507
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场