Spring Boot使用Mybatis实现真正的动态数据源切换

引入依赖

<!-- jdbc -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <!-- alibaba的druid数据库连接池 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.9</version>
        </dependency>
        <!-- mysql -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.11</version>
        </dependency>
        <!-- mybatis -->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.2</version>
        </dependency>

如果需要使用到其他数据库,则引入其他需要的依赖,不过Oracle不能直接引入使用了,需要自己下载jar包引入到项目中

yml配置默认的数据源:

spring:
  datasource:
    default:
      url: jdbc:mysql://localhost:3306/myproj?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false
      type: com.alibaba.druid.pool.DruidDataSource
      username: root
      password: 123456
      driverClassName: com.mysql.cj.jdbc.Driver
      maxActive: 20
      initialSize: 5
      testWhileIdle: true
      testOnReturn: true
      testOnBorrow: true
      timeBetweenEvictionRunsMillis: 5000
      minEvictableIdleTimeMillis: 30000
      validationQuery: SELECT 1 FROM DUAL
      minIdle: 3
      poolPreparedStatements: true
      logAbandoned: true
      removeAbandoned: true
      removeAbandonedTimeout: 1800
      filters: stat,wall

接着我们配置默认数据源

import com.alibaba.druid.pool.DruidDataSource;
import com.datasource.DynamicDataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

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

@Configuration
public class DataSourceConfig {
    @Value("${spring.datasource.default.url}")
    private String defaultDBUrl;
    @Value("${spring.datasource.default.username}")
    private String defaultDBUser;
    @Value("${spring.datasource.default.password}")
    private String defaultDBPassword;
    @Value("${spring.datasource.default.driverClassName}")
    private String defaultDBDriverName;
    @Value("${spring.datasource.default.validationQuery}")
    private String validationQuery;
    @Value("${spring.datasource.default.testOnBorrow}")
    private boolean testOnBorrow;
    @Value("${spring.datasource.default.testOnReturn}")
    private boolean testOnReturn;
    @Value("${spring.datasource.default.testWhileIdle}")
    private boolean testWhileIdle;
    @Value("${spring.datasource.default.filters}")
    private String filters;
    @Value("${spring.datasource.default.maxActive}")
    private int maxActive;

    @Bean
    public DynamicDataSource dynamicDataSource() {
        DynamicDataSource dynamicDataSource = DynamicDataSource.getInstance();
        try {
            DruidDataSource defaultDataSource = new DruidDataSource();
            defaultDataSource.setUrl(defaultDBUrl);
            defaultDataSource.setUsername(defaultDBUser);
            defaultDataSource.setPassword(defaultDBPassword);
            defaultDataSource.setDriverClassName(defaultDBDriverName);
            defaultDataSource.setValidationQuery(validationQuery);
            defaultDataSource.setMaxActive(maxActive);
            defaultDataSource.setTestOnBorrow(testOnBorrow);
            defaultDataSource.setTestOnReturn(testOnReturn);
            defaultDataSource.setTestWhileIdle(testWhileIdle);
            defaultDataSource.setFilters(filters);
            Map<Object, Object> map = new HashMap<>();
            map.put("default", defaultDataSource);
            dynamicDataSource.setTargetDataSources(map);
            dynamicDataSource.setDefaultTargetDataSource(defaultDataSource);
        } catch (Exception ex) {
            ex.printStackTrace();
        }

        return dynamicDataSource;
    }

    @Bean
    public SqlSessionFactory sqlSessionFactory(
            @Qualifier("dynamicDataSource") DataSource dynamicDataSource)
            throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dynamicDataSource);
        bean.setMapperLocations(new PathMatchingResourcePatternResolver()
                .getResources("classpath*:mapper/*.xml"));
        return bean.getObject();

    }

    @Bean(name = "sqlSessionTemplate")
    public SqlSessionTemplate sqlSessionTemplate(
            @Qualifier("sqlSessionFactory") SqlSessionFactory sqlSessionFactory)
            throws Exception {
        return new SqlSessionTemplate(sqlSessionFactory);
    }
}

这样在未切换数据源的情况下默认使用默认数据源
自定义数据源DataSource类

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

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

public class DynamicDataSource extends AbstractRoutingDataSource {
    private static DynamicDataSource instance;
    private static byte[] lock = new byte[0];
    private static Map<Object, Object> dataSourceMap = new HashMap<>();

    @Override
    public void setTargetDataSources(Map<Object, Object> targetDataSources) {
        super.setTargetDataSources(targetDataSources);
        dataSourceMap.putAll(targetDataSources);
        super.afterPropertiesSet();// 必须添加该句,否则新添加数据源无法识别到
    }

    public Map<Object, Object> getDataSourceMap() {
        return dataSourceMap;
    }

    public static synchronized DynamicDataSource getInstance() {
        if (instance == null) {
            synchronized (lock) {
                if (instance == null) {
                    instance = new DynamicDataSource();
                }
            }
        }
        return instance;
    }

    @Override
    protected Object determineCurrentLookupKey() {
        return DataSourceContextHolder.getDBType();
    }

}

通过ThreadLocal维护一个全局唯一的map来实现数据源的动态切换


public class DataSourceContextHolder {
    private static final ThreadLocal<String> threadLocal = new ThreadLocal();

    public static synchronized void setDBType(String data) {
        threadLocal.set(data);
    }

    public static String getDBType() {
        return threadLocal.get();
    }
}

使用定时任务读取在默认数据库里设置好的其他数据库的信息

import com.alibaba.druid.pool.DruidDataSource;
import com..entity.DataSourceInfoEntity;
import com..service.DataSourceInfoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Component
public class InitTargetDataSources {
 @Autowired
    private DataSourceInfoService dataSourceInfoService;
    /**
     * 周期性的读取
     */
   // @Scheduled(cron = "0 0/2 * * * ?")
    public void readDataSourcePeriodically() {
        setDataSourceInfo();
    }

    private void setDataSourceInfo() {
        DataSourceContextHolder.setDBType("default");
        List<DataSourceInfoEntity> list = dataSourceInfoService.getList();
        //使用自己设置的sourceName作为数据源的key,使用这个进行切换
        Map<Object, Object> dataSourceMap = list.stream().collect(Collectors.toMap(x -> x.getDatasourceName(), x -> {
            DruidDataSource druidDataSource = new DruidDataSource();
            druidDataSource.setDriverClassName(x.getDriverClassName());
            druidDataSource.setUrl(x.getDatasourceUrl());
            druidDataSource.setUsername(x.getDatasourceUsername());
            druidDataSource.setPassword(x.getDatasourcePassword());
            druidDataSource.setValidationQuery("SELECT 1 FROM DUAL");
            druidDataSource.setKeepAlive(true);
            return druidDataSource;
        }));
        DynamicDataSource dynamicDataSource = DynamicDataSource.getInstance();
        Map<Object, Object> map = dynamicDataSource.getDataSourceMap();
        map.putAll(dataSourceMap);
        dynamicDataSource.setTargetDataSources(map);
        dynamicDataSource.afterPropertiesSet();
    }
    }

public class DataSourceInfoEntity {
    private int id;
    private String datasourceName;
    private String driverClassName;
    private String datasourceUrl;
    private String datasourceUsername;
    private String datasourcePassword;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getDatasourceName() {
        return datasourceName;
    }

    public void setDatasourceName(String datasourceName) {
        this.datasourceName = datasourceName;
    }

    public String getDriverClassName() {
        return driverClassName;
    }

    public void setDriverClassName(String driverClassName) {
        this.driverClassName = driverClassName;
    }

    public String getDatasourceUrl() {
        return datasourceUrl;
    }

    public void setDatasourceUrl(String datasourceUrl) {
        this.datasourceUrl = datasourceUrl;
    }

    public String getDatasourceUsername() {
        return datasourceUsername;
    }

    public void setDatasourceUsername(String datasourceUsername) {
        this.datasourceUsername = datasourceUsername;
    }

    public String getDatasourcePassword() {
        return datasourcePassword;
    }

    public void setDatasourcePassword(String datasourcePassword) {
        this.datasourcePassword = datasourcePassword;
    }
}
@Service
public class DataSourceInfoServiceImpl implements DataSourceInfoService {

    @Autowired
    private DataSourceMapper dataSourceMapper;

    @Override
    public List<DataSourceInfoEntity> getList() {
        DataSourceContextHolder.setDBType("default");
        return dataSourceMapper.getList();
    }
    }
@Mapper
public interface DataSourceMapper {
    @Select("SELECT * FROM datasource_info")
    List<DataSourceInfoEntity> getList();
}

数据库结构
在这里插入图片描述
现在项目中已经存入了设定好的数据源
在数据库的操作前进行数据源的切换,就可以在不同的数据库中进行操作,比如从test1数据库查询数据后,再将数据插入到test2数据库中


        DataSourceContextHolder.setDBType("test1");
		List list=service.getList();

        DataSourceContextHolder.setDBType("test2");
        service.insert(list);
        具体业务具体操作。大概就是这么用的,不过在数据库操作前一定要切换到对的数据源,不然可能会弄岔了

原博客,更多更详细:
https://blog.csdn.net/YHYR_YCY/article/details/78894940

  • 4
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值