美文网首页
SpringBoot配置多数据源

SpringBoot配置多数据源

作者: 轻轻敲醒沉睡的心灵 | 来源:发表于2021-05-20 11:29 被阅读0次

配置多数据源,可以自己实现,也可以使用别人已有的轮子。

1. 自定义注解配置多数据源

自己配置多数据源用到了自定义注解,参考文章:
Java注解 - 简书 (jianshu.com)

1.1 需要的基本依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.4.2</version>
</dependency>
<!-- druid -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.2.5</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
    <optional>true</optional>
</dependency>

1.2 配置多数据源

因为引入了druid,是在druid的基础上配置的。

  • 1.2.1 application.yml文件中数据库信息:
spring: 
  # datasource
  datasource:
    druid:
      # 数据源1
      master:
        url: jdbc:mysql://192.168.0.1:3306/test?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8&allowMultiQueries=true&autoReconnect=true
        password: root
        username: root
        driver-class-name: com.mysql.cj.jdbc.Driver 
        initial-size: 5
        max-active: 20
        min-idle: 5
        max-wait: 6000
      # 数据源2
      slave: 
        # 多数据源开关,配合@ConditionalOnProperty使用
        enabled : true
        url: jdbc:mysql://192.168.0.2:3306/test?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8&allowMultiQueries=true&autoReconnect=true
        username: root
        password: root
        driver-class-name: com.mysql.cj.jdbc.Driver
        #  连接池配置
        # 配置初始化大小、最小、最大
        initial-size: 5
        max-active: 20
        min-idle: 5
        # 连接超时时间
        max-wait: 6000
  • 1.2.2 配置动态数据源

动态数据源-继承AbstractRoutingDataSource类,重写方法:方法内容不重要,如果有需要,可以自己打印一些日志,

package com.test.dynamic.datasource.test.datasource;

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

public class DynamicDataSource extends AbstractRoutingDataSource {

    @Override
    protected Object determineCurrentLookupKey() {
        return DynamicDataSourceContextHolder.getDataSourceType();
    }
}

配置多个datasource,添加到动态数据源中

package com.test.dynamic.datasource.test.datasource;

import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
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 com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;

import java.util.HashMap;
import java.util.Map;

import javax.sql.DataSource;

/**
 * @date 2021年5月20日 上午10:16:09
 * @Description:多数据源配置
 */
@Configuration
@EnableAutoConfiguration(exclude = { DataSourceAutoConfiguration.class })
public class DynamicDataSourceConfig {
    
    @Bean
    @ConfigurationProperties("spring.datasource.druid.master")
    public DataSource masterDataSource() {
        return DruidDataSourceBuilder.create().build();
    }
    
    /**
     * @ConditionalOnProperty一般加在@Configurarion、@Component配置的类上或@Bean配置的方法上,
     * 表示满足获取到某些配置文件信息后才会配置或加载。
     * havingValue可与name组合使用:比较name获取到的属性值与havingValue给定的值是否相同,相同才加载配置
     */
    @Bean
    @ConfigurationProperties("spring.datasource.druid.slave")
    @ConditionalOnProperty(prefix = "spring.datasource.druid.slave", name = "enabled", havingValue = "true")
    public DataSource slaveDataSource() {
        return DruidDataSourceBuilder.create().build();
    }

    @Bean
    @Primary
    public DataSource dynamicDataSource() {
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        // 添加数据源
        Map<Object, Object> targetDataSources = new HashMap<>();
        DataSource masterDataSource = masterDataSource();
        DataSource slaveDataSource = slaveDataSource();
        targetDataSources.put(DataSourceType.MASTER.name(), masterDataSource);
        targetDataSources.put(DataSourceType.SLAVE.name(), slaveDataSource);
        dynamicDataSource.setTargetDataSources(targetDataSources);
        // 设置默认数据源
        dynamicDataSource.setDefaultTargetDataSource(masterDataSource);
        return dynamicDataSource;
    }
}

注意:因为是自己装配数据源,要排除SpringBoot自动装配数据源,所以类上添加注解@EnableAutoConfiguration(exclude = { DataSourceAutoConfiguration.class }),否则报错

1.3 数据源手动切换使用

前面将数据源添加到了动态数据源(DynamicDataSource)中,并且设置了主数据源为默认数据源。但我们想要的是在需要的地方,想切换就切换。因此,需要有一个动态获切换数据源的地方(我们称为上下文),对于 web 应用,访问以线程为单位,使用 ThreadLocal 就比较合适,如下:

package com.test.dynamic.datasource.test.datasource;

public class DynamicDataSourceContextHolder {
    
    /**
     * 使用ThreadLocal维护变量,ThreadLocal为每个使用该变量的线程提供独立的变量副本,
     * 所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
     */
    private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();

    /**
     * 设置数据源的变量
     */
    public static void setDataSourceType(String dsType) {
        CONTEXT_HOLDER.set(dsType);
    }

    /**
     * 获得数据源的变量
     */
    public static String getDataSourceType() {
        return CONTEXT_HOLDER.get();
    }

    /**
     * 清空数据源变量
     */
    public static void clearDataSourceType() {
        CONTEXT_HOLDER.remove();
    }
}

使用时,在DAO前面先切换到目标数据源:

package com.test.dynamic.datasource.test.service.impl;

import java.util.Date;
import java.util.List;

import javax.annotation.Resource;

import org.springframework.stereotype.Service;

import com.test.dynamic.datasource.test.dao.UserDao;
import com.test.dynamic.datasource.test.entity.Result;
import com.test.dynamic.datasource.test.entity.User;
import com.test.dynamic.datasource.test.service.TestService;


@Service
public class TestServiceImpl implements TestService {
    
    @Resource
    private UserDao userDao;

    @Override
    public Result<String> addUser(String name) {
        User u = new User();
        u.setCreateTime(new Date());
        u.setIdCard("1234567891011");
        u.setName(name);
        u.setPhone("123");
                // 手动切换数据源
        DynamicDataSourceContextHolder.setDataSourceType(DataSourceType.SLAVE.name());
        userDao.addUser(u);
                // 手动切换数据源
        DynamicDataSourceContextHolder.setDataSourceType(DataSourceType.MASTER.name());
        List<User> list = userDao.listMyUser();
        for (User user : list) {
            System.out.println(user);
        }
        return new Result<String>().success(1);
    }
}

注意:到这就能使用了,但是每次sql时要手动切换数据源。下面讲用注解来切换数据源......

1.4 写数据源注解

数据源枚举类

package com.test.dynamic.datasource.test.datasource;

public enum DataSourceType {
    
    /**
     * 主库
     */
    MASTER,

    /**
     * 从库
     */
    SLAVE;

}

数据源注解类

package com.test.dynamic.datasource.test.datasource;

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 {
    
    public DataSourceType value() default DataSourceType.MASTER;

}

1.5 AOP+注解自动切换数据源

AOP类

package com.test.dynamic.datasource.test.datasource;

import java.lang.reflect.Method;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

@Aspect
@Order(1)
@Component
public class DynamicDataSourceAspect {

    // 配置织入点
    @Pointcut("@annotation(com.test.dynamic.datasource.test.datasource.DataSource)")
    public void pointCut() {
        
    }
        
    /**
     * @Description:在方法执行执行会先拦截注解@DataSource这个的方法
     */
    @Before("pointCut()")
    public void changeDataSource(JoinPoint point) {
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        DataSource ds = method.getAnnotation(DataSource.class);
        // 获取注解中的数据源
        String dsType = ds.value().name();
        if (!dsType.equals(DynamicDataSourceContextHolder.getDataSourceType())) {
            DynamicDataSourceContextHolder.setDataSourceType(dsType);
        }
    }
    
    
    /**
     * @Description:执行完成后将数据源切换为主数据源,这样使用主数据源不用加注解
     */
    @After("pointCut()")
    public void releaseLocal(JoinPoint point) {
        if (!DataSourceType.MASTER.name().equals(DynamicDataSourceContextHolder.getDataSourceType())) {
            DynamicDataSourceContextHolder.setDataSourceType(DataSourceType.MASTER.name());
        }
    }
}

DAO接口中添加@DataSource注解使用
使用注解就不用在service实现类中手动切换了,即DynamicDataSourceContextHolder.setDataSourceType(DataSourceType.MASTER.name());可以去掉。

package com.test.dynamic.datasource.test.dao;

import java.util.List;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.test.dynamic.datasource.test.datasource.DataSource;
import com.test.dynamic.datasource.test.datasource.DataSourceType;
import com.test.dynamic.datasource.test.entity.User;

public interface UserDao extends BaseMapper<User> {
    
    @DataSource(DataSourceType.SLAVE)
    Integer addUser(User u);
    
    @DataSource(DataSourceType.SLAVE)
    List<User> listUser();
    
    Integer addMyUser(User u);
    
    List<User> listMyUser();

}

2. Mybatis-plus扩展插件配置多数据源

包米豆的插件dynamic-datasource-spring-boot-starter提供了多数据源功能,官网使用方法比较详细,可以参考使用。
参考:Mybatis-plus多数据源插件

相关文章

网友评论

      本文标题:SpringBoot配置多数据源

      本文链接:https://www.haomeiwen.com/subject/vfbtjltx.html