java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > mybatis-plus动态数据源读写分离

mybatis-plus动态数据源读写分离方式

作者:杨村你喜哥l

在分布式项目开发中,动态数据源的配置与使用至关重要,通过创建DynamicDatasourceService,实现数据源的动态添加与调用,有效管理主从库操作,减轻数据库压力,此外,通过配置类与@DS注解,实现了灵活的分库查询功能,为高效处理数据提供了强有力的支持

1、背景

在实际项目的开发过程中,一定会存在主库与从库的分布式模式,主库进行增删改,从库进行查询。

这样可以保证对不同的数据库进行操作,减少对数据库的压力。

2、动态数据源

创建DynamicDatasourceService

import com.jiuqi.grid.collection.entity.datasource.DataSourceDTO;
import java.util.Set;
public interface DynamicDatasourceService {


    /**
     * 获取所有数据源
     *
     * @return
     */
    Set<String> datasources();


    /**
     * 添加数据源
     *
     * @param dto 数据源信息
     * @return
     */
    Set<String> add(DataSourceDTO dto);


    /**
     * 移除数据源
     *
     * @param name 连接池名称
     */
    void remove(String name);
}

实现DynamicDatasourceService

package com.jiuqi.grid.collection.service.impl;

import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
import com.baomidou.dynamic.datasource.creator.DefaultDataSourceCreator;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DataSourceProperty;
import com.jiuqi.grid.collection.entity.datasource.DataSourceDTO;
import com.jiuqi.grid.collection.service.DynamicDatasourceService;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import javax.sql.DataSource;
import java.util.Set;

@Service
public class DynamicDatasourceServiceImpl implements DynamicDatasourceService {


    private final DataSource dataSource;

    private final DefaultDataSourceCreator dataSourceCreator;

    public DynamicDatasourceServiceImpl(
            DataSource dataSource, DefaultDataSourceCreator dataSourceCreator) {
        this.dataSource = dataSource;
        this.dataSourceCreator = dataSourceCreator;
    }


    /**
     * 获取所有数据源
     *
     * @return
     */
    @Override
    public Set<String> datasources() {
        DynamicRoutingDataSource ds = (DynamicRoutingDataSource) dataSource;
        return ds.getDataSources().keySet();
    }


    /**
     * 添加数据源
     *
     * @param dto
     * @return
     */
    @Override
    public Set<String> add(DataSourceDTO dto) {
        DataSourceProperty dataSourceProperty = new DataSourceProperty();
        BeanUtils.copyProperties(dto, dataSourceProperty);
        DynamicRoutingDataSource ds = (DynamicRoutingDataSource) dataSource;
        DataSource dataSource = dataSourceCreator.createDataSource(dataSourceProperty);
        ds.addDataSource(dto.getPollName(), dataSource);
        return ds.getDataSources().keySet();
    }


    /**
     * 删除数据源
     *
     * @param name
     */
    @Override
    public void remove(String name) {
        DynamicRoutingDataSource ds = (DynamicRoutingDataSource) dataSource;
        ds.removeDataSource(name);
    }

}

2.1添加数据源

我这里是读取的配置文件里的用户名、密码等数据源的链接

 public void addDatasource(String tenant) {
        if (dynamicDatasourceService.datasources().contains(tenant)) {
            return;
        }
        DataSourceDTO datasourceDTO = new DataSourceDTO();
        //库名
        datasourceDTO.setPollName(tenant);
        //链接信息 读取配置文件的主库信息
        datasourceDTO.setDriverClassName(sqlDataSource.getDriverClassName());
        //替换数据库
        if (StringUtils.isEmpty(sqlDataSource.getUrl())) {
            throw new RuntimeException("配置文件读取失败");
        }
        String url = sqlDataSource.getUrl();
        url = url.replace("grid_test", tenant);
        datasourceDTO.setUrl(url);
        datasourceDTO.setUsername(sqlDataSource.getUsername());
        datasourceDTO.setPassword(sqlDataSource.getPassword());
        dynamicDatasourceService.add(datasourceDTO);
    }

2.2数据源

调用上面的addDatasource()方法,数据源就会创建成功,这里要注意的是,每一次增删改查都需要调用此方法,以防数据源不存在。

3、分库查询

创建新的数据源目的就是为了可以进行分库操作。

分库操作步骤如下:

3.1添加配置类

package com.jiuqi.grid.collection.dsprocessor;

import com.baomidou.dynamic.datasource.processor.DsHeaderProcessor;
import com.baomidou.dynamic.datasource.processor.DsProcessor;
import com.baomidou.dynamic.datasource.processor.DsSessionProcessor;
import com.baomidou.dynamic.datasource.processor.DsSpelExpressionProcessor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 注册动态数据源解析器
 */
@Configuration
public class MyDynamicDataSourceConfig {

  @Bean
  public DsProcessor dsProcessor() {
    DsMapProcessor mapProcessor = new DsMapProcessor();
    DsHeaderProcessor headerProcessor = new DsHeaderProcessor();
    DsSessionProcessor sessionProcessor = new DsSessionProcessor();
    DsSpelExpressionProcessor spelExpressionProcessor = new DsSpelExpressionProcessor();
    mapProcessor.setNextProcessor(headerProcessor);
    headerProcessor.setNextProcessor(sessionProcessor);
    sessionProcessor.setNextProcessor(spelExpressionProcessor);
    return mapProcessor;
  }
}
package com.jiuqi.grid.collection.dsprocessor;

import com.baomidou.dynamic.datasource.processor.DsProcessor;
import java.util.Map;
import com.jiuqi.grid.collection.consts.SQLConstants;
import com.jiuqi.grid.collection.dto.common.Address3DTO;
import com.jiuqi.grid.collection.dto.data.CollectionDataPageParam;
import com.jiuqi.grid.collection.entity.CollectionDataDO;
import org.aopalliance.intercept.MethodInvocation;

/**
 * 通过params map中获取数据源标识
 *
 * @author songlude
 */
public class DsMapProcessor extends DsProcessor {

    /**
     * 抽象匹配条件 匹配才会走当前执行器否则走下一级执行器
     *
     * @param key DS注解里的内容
     * @return 是否匹配
     */
    @Override
    public boolean matches(String key) {
        return key.equals("#" + SQLConstants.EXEC_DATASOURCE);
    }

    /**
     * 抽象最终决定数据源
     *
     * @param invocation 方法执行信息
     * @param key        DS注解里的内容
     * @return 数据源名称
     */
    @Override
    public String doDetermineDatasource(MethodInvocation invocation, String key) {
        Object[] arguments = invocation.getArguments();
        Object argument = arguments[0];

        if (argument instanceof Map) {
            Map<String, Object> params = (Map<String, Object>) argument;
            Object value = params.get(SQLConstants.EXEC_DATASOURCE);
            return value == null ? null : value.toString();
        } else if (argument instanceof CollectionDataPageParam) {
            CollectionDataPageParam collectionDataPageParam = (CollectionDataPageParam) argument;
            return collectionDataPageParam.getGRID_DATASOURCE();
        } else if (argument instanceof CollectionDataDO) {
            CollectionDataDO collectionDataDO = (CollectionDataDO) argument;
            return collectionDataDO.getGRID_DATASOURCE();
        } else if (argument instanceof Address3DTO) {
            Address3DTO address3DTO = (Address3DTO) argument;
            return address3DTO.getGRID_DATASOURCE();
        }
        return null;
    }
}

这里要注意需要把“argument”进行类型的判断,argument 就是你查询Mapper的参数,这里我获取的是argument[0],则Mapper参数的位置在第一个。

3.2 添加数据源查询

通过@DS注解来指定数据源,在配置类中指定了以#开头

 @DS("#GRID_DATASOURCE")
    @UpdateProvider(type = QueryProvider.class, method = "createDatabase")
    void createDatabase(Map<String, Object> params);

这样就可以开启你的动态数据源,分库查询啦。

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

您可能感兴趣的文章:
阅读全文