首页 > 其他分享 >Spring Boot 多数据源配置

Spring Boot 多数据源配置

时间:2022-11-23 15:27:56浏览次数:80  
标签:cjs spring 数据源 Boot datasource Spring import com example

第一种方式: AbstractRoutingDataSource

1.1. 手动切换数据源

application.properties

# Order
# 如果用Druid作为数据源,应该用url属性,而不是jdbc-url
spring.datasource.order.jdbc-url=jdbc:mysql://localhost:3306/order?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&useSSL=false
spring.datasource.order.username=root
spring.datasource.order.password=123456
spring.datasource.order.driver-class-name=com.mysql.cj.jdbc.Driver
# Stock
spring.datasource.stock.jdbc-url=jdbc:mysql://localhost:3306/stock?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&useSSL=false
spring.datasource.stock.username=root
spring.datasource.stock.password=123456
spring.datasource.stock.driver-class-name=com.mysql.cj.jdbc.Driver
# Account
spring.datasource.account.jdbc-url=jdbc:mysql://localhost:3306/account?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&useSSL=false
spring.datasource.account.username=root
spring.datasource.account.password=123456
spring.datasource.account.driver-class-name=com.mysql.cj.jdbc.Driver

配置数据源

DataSourceConfig.java

package com.cjs.example.config;

import com.alibaba.druid.pool.DruidDataSource;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import com.zaxxer.hikari.HikariDataSource;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

@Configuration
public class DataSourceConfig {

    @Bean("orderDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.order")
    public DataSource orderDataSource() {
//        return new HikariDataSource();
//        return new DruidDataSource();
        return DataSourceBuilder.create().build();
    }

    @Bean("accountDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.account")
    public DataSource accountDataSource() {
//        return new HikariDataSource();
//        return new DruidDataSource();
        return DataSourceBuilder.create().build();
    }

    @Bean("stockDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.stock")
    public DataSource stockDataSource() {
//        return new HikariDataSource();
//        return new DruidDataSource();
        return DataSourceBuilder.create().build();
    }

    @Primary
    @Bean("dynamicDataSource")
    public DataSource dynamicDataSource(@Qualifier("orderDataSource") DataSource orderDataSource,
                                        @Qualifier("accountDataSource") DataSource accountDataSource,
                                        @Qualifier("stockDataSource") DataSource stockDataSource) {

        Map<Object, Object> dataSourceMap = new HashMap<>(3);
        dataSourceMap.put(DataSourceKey.ORDER.name(), orderDataSource);
        dataSourceMap.put(DataSourceKey.STOCK.name(), stockDataSource);
        dataSourceMap.put(DataSourceKey.ACCOUNT.name(), accountDataSource);

        DynamicRoutingDataSource dynamicRoutingDataSource = new DynamicRoutingDataSource();
        dynamicRoutingDataSource.setDefaultTargetDataSource(orderDataSource);
        dynamicRoutingDataSource.setTargetDataSources(dataSourceMap);

        return dynamicRoutingDataSource;
    }

    /* https://baomidou.com/pages/3b5af0/ */
    @Bean
    public MybatisSqlSessionFactoryBean sqlSessionFactoryBean(@Qualifier("dynamicDataSource") DataSource dataSource) {
        MybatisSqlSessionFactoryBean sqlSessionFactoryBean = new MybatisSqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
//        sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mappers/*.xml"));
        return sqlSessionFactoryBean;
    }
}

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

DataSourceKey.java

package com.cjs.example.config;

public enum DataSourceKey {
    /**
     * Order data source key.
     */
    ORDER,
    /**
     * Stock data source key.
     */
    STOCK,
    /**
     * Account data source key.
     */
    ACCOUNT
}

DynamicDataSourceContextHolder.java

package com.cjs.example.config;

public class DynamicDataSourceContextHolder {

    private static final ThreadLocal<String> CONTEXT_HOLDER = ThreadLocal.withInitial(DataSourceKey.ORDER::name);

    public static void setDataSourceKey(DataSourceKey key) {
        CONTEXT_HOLDER.set(key.name());
    }

    public static String getDataSourceKey() {
        return CONTEXT_HOLDER.get();
    }

    public static void clearDataSourceKey() {
        CONTEXT_HOLDER.remove();
    }

}

DynamicRoutingDataSource.java

package com.cjs.example.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

@Slf4j
public class DynamicRoutingDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        log.info("当前数据源 [{}]", DynamicDataSourceContextHolder.getDataSourceKey());
        return DynamicDataSourceContextHolder.getDataSourceKey();
    }
}

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

DynamicDataSourceContextHolder.setDataSourceKey(DataSourceKey.ACCOUNT);

举个例子:

package com.cjs.example;

import com.cjs.example.account.entity.Account;
import com.cjs.example.account.service.IAccountService;
import com.cjs.example.config.DataSourceKey;
import com.cjs.example.config.DynamicDataSourceContextHolder;
import com.cjs.example.order.entity.Order;
import com.cjs.example.order.service.IOrderService;
import com.cjs.example.stock.entity.Stock;
import com.cjs.example.stock.service.IStockService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.math.BigDecimal;

@SpringBootTest
public class Demo1122ApplicationTests {

	@Autowired
	private IOrderService orderService;
	@Autowired
	private IAccountService accountService;
	@Autowired
	private IStockService stockService;

	@Test
	public void doBusiness() {
		DynamicDataSourceContextHolder.setDataSourceKey(DataSourceKey.ORDER);
		Order order = new Order();
		order.setOrderNo("123");
		order.setUserId("1");
		order.setCommodityCode("abc");
		order.setCount(1);
		order.setAmount(new BigDecimal("9.9"));
		orderService.save(order);

		DynamicDataSourceContextHolder.setDataSourceKey(DataSourceKey.STOCK);
		Stock stock = new Stock();
		stock.setId(1);
		stock.setCommodityCode("abc");
		stock.setName("huawei");
		stock.setCount(1);
		stockService.updateById(stock);

		DynamicDataSourceContextHolder.setDataSourceKey(DataSourceKey.ACCOUNT);
		Account account = new Account();
		account.setId(1);
		account.setUserId("1");
		account.setAmount(new BigDecimal(100));
		accountService.updateById(account);
	}

}

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

1.2. 自动切换数据源

https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#aop-ataspectj

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

1、添加@EnableAspectJAutoProxy注解

package com.cjs.example;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@EnableAspectJAutoProxy
@MapperScan("com.cjs.example.*.mapper")
@SpringBootApplication
public class Demo1122Application {
	public static void main(String[] args) {
		SpringApplication.run(Demo1122Application.class, args);
	}
}

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

package com.cjs.example.aop;

import com.cjs.example.config.DataSourceKey;
import com.cjs.example.config.DynamicDataSourceContextHolder;
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.springframework.stereotype.Component;

@Aspect
@Component
public class DataSourceAdvice {

//    @Pointcut("within(com.cjs.example.order..*)")
    @Pointcut("execution(* com.cjs.example.order..*.*(..))")
    public void orderPointcut() {}

//    @Pointcut("within(com.cjs.example.account..*)")
    @Pointcut("execution(* com.cjs.example.account..*.*(..))")
    public void accountPointcut() {}

//    @Pointcut("within(com.cjs.example.stock..*)")
    @Pointcut("execution(* com.cjs.example.stock..*.*(..))")
    public void stockPointcut() {}


    @Around("orderPointcut()")
    public Object order(ProceedingJoinPoint pjp) throws Throwable {
        DynamicDataSourceContextHolder.setDataSourceKey(DataSourceKey.ORDER);
        Object retVal = pjp.proceed();
        DynamicDataSourceContextHolder.clearDataSourceKey();
        return retVal;
    }
    @Around("accountPointcut()")
    public Object account(ProceedingJoinPoint pjp) throws Throwable {
        DynamicDataSourceContextHolder.setDataSourceKey(DataSourceKey.ACCOUNT);
        Object retVal = pjp.proceed();
        DynamicDataSourceContextHolder.clearDataSourceKey();
        return retVal;
    }
    @Around("stockPointcut()")
    public Object stock(ProceedingJoinPoint pjp) throws Throwable {
        DynamicDataSourceContextHolder.setDataSourceKey(DataSourceKey.STOCK);
        Object retVal = pjp.proceed();
        DynamicDataSourceContextHolder.clearDataSourceKey();
        return retVal;
    }
}

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

工程结构

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

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

https://github.com/baomidou/dynamic-datasource-spring-boot-starter

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

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
    <version>3.5.2</version>
</dependency>

2、配置数据源

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

主从配置,读写分离

# 多主多从                      纯粹多库(记得设置primary)                   混合配置
spring:                               spring:                               spring:
  datasource:                           datasource:                           datasource:
    dynamic:                              dynamic:                              dynamic:
      datasource:                           datasource:                           datasource:
        master_1:                             mysql:                                master:
        master_2:                             oracle:                               slave_1:
        slave_1:                              sqlserver:                            slave_2:
        slave_2:                              postgresql:                           oracle_1:
        slave_3:                              h2:                                   oracle_2:

改造一下前面的例子

spring.datasource.dynamic.primary=order
# Order
spring.datasource.dynamic.datasource.order.url=jdbc:mysql://localhost:3306/order?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&useSSL=false
spring.datasource.dynamic.datasource.order.username=root
spring.datasource.dynamic.datasource.order.password=123456
spring.datasource.dynamic.datasource.order.driver-class-name=com.mysql.cj.jdbc.Driver
# Stock
spring.datasource.dynamic.datasource.stock.url=jdbc:mysql://localhost:3306/stock?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&useSSL=false
spring.datasource.dynamic.datasource.stock.username=root
spring.datasource.dynamic.datasource.stock.password=123456
spring.datasource.dynamic.datasource.stock.driver-class-name=com.mysql.cj.jdbc.Driver
# Account
spring.datasource.dynamic.datasource.account.url=jdbc:mysql://localhost:3306/account?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&useSSL=false
spring.datasource.dynamic.datasource.account.username=root
spring.datasource.dynamic.datasource.account.password=123456
spring.datasource.dynamic.datasource.account.driver-class-name=com.mysql.cj.jdbc.Driver

3、使用 @DS 切换数据源

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

注解 结果
没有@DS 默认数据源
@DS("dsName")   dsName可以为组名也可以为具体某个库的名称
package com.cjs.example.stock.service.impl;

import com.baomidou.dynamic.datasource.annotation.DS;
import com.cjs.example.stock.entity.Stock;
import com.cjs.example.stock.mapper.StockMapper;
import com.cjs.example.stock.service.IStockService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;

@DS("stock")
@Service
public class StockServiceImpl extends ServiceImpl<StockMapper, Stock> implements IStockService {

}
package com.cjs.example.account.service.impl;

import com.baomidou.dynamic.datasource.annotation.DS;
import com.cjs.example.account.entity.Account;
import com.cjs.example.account.mapper.AccountMapper;
import com.cjs.example.account.service.IAccountService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;

@DS("account")
@Service
public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> implements IAccountService {

}

 

标签:cjs,spring,数据源,Boot,datasource,Spring,import,com,example
From: https://www.cnblogs.com/cjsblog/p/16917914.html

相关文章

  • java web开发(第一个spring程序)
        提到javaweb编程,好像spring就躲不开了。一般认为,spring有两个特征,分别是ioc、aop。两个英文单词的中文解释都比较拗口,一个称之为控制反转,一个是面向切面。对于......
  • Spring Cache + Redis;用Spring Cache的注解自动管理Redis缓存
    https://blog.csdn.net/qq_45839663/article/details/127209491?spm=1001.2101.3001.6650.2&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EYuanLiJiHu......
  • springcloud -nacos-配置中心-接入
    1.nacaosClient接入:pom.xml引入依赖<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-actuator</artifactId>......
  • Spring Data(数据) Couchbase(二)
    5.4.7.仓库方法的空处理从SpringData2.0开始,返回单个聚合实例的存储库CRUD方法使用Java8来指示可能缺少值。除此之外,SpringData还支持在查询方法上返回以下包装器类......
  • 聊聊Spring中的数据绑定DataBinder
    数据绑定 这个概念在任何一个成型的框架中都是特别重要的(尤其是web框架),它能让框架更多的自动化,更好容错性以及更高的编码效率。它提供的能力是:把字符串形式的参数转换成服......
  • Java基础__Spring思想
    IoC控制反转对象的创建控制权由程序转移到外部(解耦),Spring对IoC思想进行了实现Spring提供了一个容器,IoC容器,用于充当IoC思想的外部,被创建的对象在Io......
  • Apache Solr 的 Spring Data (数据)
    版本4.3.15SpringDataforApacheSolr项目通过使用ApacheSolr搜索引擎将Spring的核心概念应用于解决方案的开发。我们提供了一个“模板”作为存储和查询文档的高级抽象......
  • SpringBatch 框架
    SpringBatch是一个轻量级的、完善的批处理框架,旨在帮助企业建立健壮、高效的批处理应用。它是一个批处理应用框架,不是调度框架,需要配合调度框架来完成批处理工作。这里......
  • Springboot 打包细节
    这里是打包关键备注一篇文章带你认识SpringBoot打包成的可执行jar,不能被其他项目依赖https://blog.csdn.net/nanhuaibeian/article/details/109310112需要注意,......
  • Spring Security(3)
    您好,我是湘王,这是我的博客园,欢迎您来,欢迎您再来~ 前面运行写好的代码之所以没有任何显示,是因为还没有对SpringSecurity进行配置,当然啥也不显示了。这就好比你坐在车上,却......