springboot + mybatis 配置多数据源 思路简单清晰 实战演示

一    思路

首先我们知道一个数据源可以简单的理解为一个数据库连接    我们程序要配置多数据源    那么就需要  配置多个数据库的连接信息  然后对与事务  一个数据源对应一个事务管理器   在mybatis的初始化中  它需要根据数据源创建sqlSessionFactory工厂 我们要创建多数据源 那么我们应该创建多个sqlSessionFactory与数据源对应     这个时候问题来了  我们怎么可以让不同的mapper调用不同的会话工厂呢得到会话呢    这些就是配置多数据源的步骤与原理  下面我们来依次解决这个问题

环境 springboot 2.0.x

步骤一:

首先需要创建配置文件  来得到不同的数据源配置信息   可以想到这些配置的属性名和行为都是一样的 比如创建数据源 创建session工厂 事务管理处理器   所以我们可以 可以抽取出来  作为一个父类   

@Data
@Slf4j
public class BaseSourceConfig {

    private String filters;
    private String url;
    private String username;
    private String password;
    private String driverClassName;
    private int initialSize;
    private int minIdle;
    private int maxActive;
    private long maxWait;
    private long timeBetweenEvictionRunsMillis;
    private long minEvictableIdleTimeMillis;
    private String validationQuery;
    private boolean testWhileIdle;
    private boolean testOnBorrow;
    private boolean testOnReturn;
    private boolean poolPreparedStatements;
    private int maxPoolPreparedStatementPerConnectionSize;
    private MybatisConfig mybatisConfig=new MybatisConfig();
    private Configuration configuration;
    private  String dataSourceName;


    @Data
    public static  class  MybatisConfig{

        private String [] mapperXmlLocation;

        private  String [] mapperLocation;


    }





    public DataSource dataSourse() throws SQLException {
        DruidDataSource druid = new DruidDataSource();
        // 监控统计拦截的filters
        druid.setFilters(filters);
        // 配置基本属性
        druid.setDriverClassName(driverClassName);
        druid.setUsername(username);
        druid.setPassword(password);
        druid.setUrl(url);
        druid.setName(dataSourceName);

    /*    //初始化时建立物理连接的个数
        druid.setInitialSize(initialSize);
        //最大连接池数量
        druid.setMaxActive(maxActive);
        //最小连接池数量
        druid.setMinIdle(minIdle);
        //获取连接时最大等待时间,单位毫秒。
        druid.setMaxWait(maxWait);
        //间隔多久进行一次检测,检测需要关闭的空闲连接
        druid.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
        //一个连接在池中最小生存的时间
        druid.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
        //用来检测连接是否有效的sql
        druid.setValidationQuery(validationQuery);
        //建议配置为true,不影响性能,并且保证安全性。
        druid.setTestWhileIdle(testWhileIdle);
        //申请连接时执行validationQuery检测连接是否有效
        druid.setTestOnBorrow(testOnBorrow);
        druid.setTestOnReturn(testOnReturn);
        //是否缓存preparedStatement,也就是PSCache,oracle设为true,mysql设为false。分库分表较多推荐设置为false
        druid.setPoolPreparedStatements(poolPreparedStatements);
        // 打开PSCache时,指定每个连接上PSCache的大小
        druid.setMaxPoolPreparedStatementPerConnectionSize(maxPoolPreparedStatementPerConnectionSize);*/

        return druid;
    }

    // 创建该数据源的事务管理
    public DataSourceTransactionManager dataSourseTransactionManager(DataSource dataSourse) throws SQLException {
        return new DataSourceTransactionManager(dataSourse);
    }


    // 创建Mybatis的连接会话工厂实例
    public SqlSessionFactory dataSourseSqlSessionFactory( DataSource dataSourse) throws Exception {
        DruidDataSource dataSourseR = (DruidDataSource) dataSourse;
        String name = dataSourseR.getName();
        log.info("配置数据源:{} sqlSessionFactory工厂---------------------------------------",name);
        log.info("应用的数据源地址:{}", dataSourseR.getUrl());
        final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        sessionFactory.setDataSource(dataSourse);  // 设置数据源bean

        PathMatchingResourcePatternResolver pathMatchingResourcePatternResolver = new PathMatchingResourcePatternResolver();
        List<Resource> mapperLocations=new ArrayList<>();
        Arrays.stream(mybatisConfig.mapperXmlLocation).forEach(xml->{
            try {
                Resource[] resources = pathMatchingResourcePatternResolver.getResources(xml);
                mapperLocations.addAll(Arrays.asList(resources));
            } catch (IOException e) {
                log.info("{} 文件不存在",xml);
                e.printStackTrace();
            }


        });

        log.info("扫描到数据源:{} 的mapper文件如下:-----------------------",name);
        mapperLocations.forEach(p->{
            log.info(p.getFilename());
        });


        Resource [] resources=new Resource[mapperLocations.size()];
        sessionFactory.setMapperLocations(mapperLocations.toArray(resources));  // 设置mapper文件路径
        if(configuration!=null){
            sessionFactory.setConfiguration(configuration);
        }
        log.info("配置数据源:{} sqlSessionFactory工厂配置结束---------------------------------------",name);

        return sessionFactory.getObject();
    }

  步骤二:不同的数据源继承父类 

  数据源1


/**
 * @author
 * @discription;
 * @time 2020/9/5 17:50
 */
@Data
@Configuration
@ConfigurationProperties(prefix = "spring.datasource1")
@MapperScan(basePackages = {"com.pps.back.frame.pupansheng.mapper","com.pps.back.frame.pupansheng.security.mapper"},sqlSessionFactoryRef = "dataSourse1SqlSessionFactory")
public class DataSourceConfig1 extends  BaseSourceConfig{

    @Primary
    @Bean(name = "dataSourse1")
    public DataSource dataSourse1() throws SQLException {
        return super.dataSourse();
    }

    // 创建该数据源的事务管理
    @Primary
    @Bean(name = "dataSourse1TransactionManager")
    public DataSourceTransactionManager dataSourse1TransactionManager(@Qualifier("dataSourse1") DataSource dataSourse1) throws SQLException {
        return super.dataSourseTransactionManager(dataSourse1);
    }

    // 创建Mybatis的连接会话工厂实例
    @Primary
    @Bean(name = "dataSourse1SqlSessionFactory")
    public SqlSessionFactory dataSourse1SqlSessionFactory(@Qualifier("dataSourse1") DataSource dataSourse1) throws Exception {
        return super.dataSourseSqlSessionFactory(dataSourse1);
    }


}

 数据源2:

@Data
@Configuration
@ConfigurationProperties(prefix = "spring.datasource2")
@MapperScan(basePackages = {"com.pps.back.frame.pupansheng.mapper2","com.pps.back.frame.pupansheng.security.mapper"},sqlSessionFactoryRef = "dataSourse2SqlSessionFactory")
@Slf4j
public class DataSourceConfig2 extends BaseSourceConfig {


    @Bean(name = "dataSourse2")
    public DataSource dataSourse2() throws SQLException {
        return super.dataSourse();
    }

    // 创建该数据源的事务管理
    @Bean(name = "dataSourse2TransactionManager")
    public DataSourceTransactionManager dataSourse2TransactionManager(@Qualifier("dataSourse2") DataSource dataSourse) throws SQLException {
        return super.dataSourseTransactionManager(dataSourse);
    }


    // 创建Mybatis的连接会话工厂实例
    @Bean(name = "dataSourse2SqlSessionFactory")
    public SqlSessionFactory dataSourse2SqlSessionFactory(@Qualifier("dataSourse2") DataSource dataSourse2) throws Exception {
        return super.dataSourseSqlSessionFactory(dataSourse2);
    }




}

步骤三: 配置文件填写

  datasource1:
    #   数据源基本配置
    username: root
    password: pps123
    #driver-class-name: com.mysql.jdbc.Driver
    #url: jdbc:mysql://xxxx:3306/sajt?characterEncoding=utf-8
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/webmodel1?useUnicode=true&characterEncoding=UTF-8&userSSL=false&serverTimezone=GMT%2B8
    type: com.alibaba.druid.pool.DruidDataSource
    dataSourceName: dataSource1
    mybatisConfig:
      mapperXmlLocation:
        - classpath:mybatis/security/mapper/*.xml  #xml位置
      mapperLocation:
        - com.pps.back.frame.pupansheng.mapper    #暂时未用到
  datasource2:
    #   数据源基本配置
    username: root
    password: pps123
    #driver-class-name: com.mysql.jdbc.Driver
    #url: jdbc:mysql://xxxx:3306/sajt?characterEncoding=utf-8
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/webmodel2?useUnicode=true&characterEncoding=UTF-8&userSSL=false&serverTimezone=GMT%2B8
    type: com.alibaba.druid.pool.DruidDataSource
    dataSourceName: dataSource2

    mybatisConfig:
      mapperXmlLocation:
        - classpath:mybatis/security/mapper/*.xml  #xml位置
      mapperLocation:
        - com.pps.back.frame.pupansheng.mapper2     #暂时未用到

 

 

好的此时springboot启动的时候   就可以根据不同配置信息创建不同的sessionFactory工厂在容器中 

并且我们也在创建容器的过程中指定了不同的数据源  不同的事务管理器 sessionFactory工厂  和mapper文件位置  和dao接口的包名位置   

下面分析一下数据源1的配置

其中:

指定不同的数据源   :

@Primary
@Bean(name = "dataSourse1")
public DataSource dataSourse1() throws SQLException {
    return super.dataSourse();
}

指定不同事务管理器:

// 创建该数据源1的事务管理
@Primary
@Bean(name = "dataSourse1TransactionManager")
public DataSourceTransactionManager dataSourse1TransactionManager(@Qualifier("dataSourse1") DataSource dataSourse1) throws SQLException {
    return super.dataSourseTransactionManager(dataSourse1);
}

指定不同的mybatis会话工厂 请注意 此步骤 很重要  这一步骤制定了dao接口位置和xml位置

// 创建Mybatis的连接会话工厂实例
@Primary
@Bean(name = "dataSourse1SqlSessionFactory")
public SqlSessionFactory dataSourse1SqlSessionFactory(@Qualifier("dataSourse1") DataSource dataSourse1) throws Exception {
    return super.dataSourseSqlSessionFactory(dataSourse1);
}

指定接口位置在类的注解上 @MapperScan(当然也有其他方式):

@MapperScan(basePackages = {"com.pps.back.frame.pupansheng.mapper","com.pps.back.frame.pupansheng.security.mapper"},sqlSessionFactoryRef = "dataSourse1SqlSessionFactory")

指定xml位置在数据源父类的方法 dataSourse1SqlSessionFactory()

public SqlSessionFactory dataSourseSqlSessionFactory( DataSource dataSourse) throws Exception {
    DruidDataSource dataSourseR = (DruidDataSource) dataSourse;
    String name = dataSourseR.getName();
    log.info("配置数据源:{} sqlSessionFactory工厂---------------------------------------",name);
    log.info("应用的数据源地址:{}", dataSourseR.getUrl());
    final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
    sessionFactory.setDataSource(dataSourse);  // 设置数据源bean

    PathMatchingResourcePatternResolver pathMatchingResourcePatternResolver = new PathMatchingResourcePatternResolver();
    List<Resource> mapperLocations=new ArrayList<>();
    Arrays.stream(mybatisConfig.mapperXmlLocation).forEach(xml->{
        try {
            Resource[] resources = pathMatchingResourcePatternResolver.getResources(xml);
            mapperLocations.addAll(Arrays.asList(resources));
        } catch (IOException e) {
            log.info("{} 文件不存在",xml);
            e.printStackTrace();
        }


    });

    log.info("扫描到数据源:{} 的mapper文件如下:-----------------------",name);
    mapperLocations.forEach(p->{
        log.info(p.getFilename());
    });


    Resource [] resources=new Resource[mapperLocations.size()];
    sessionFactory.setMapperLocations(mapperLocations.toArray(resources));  // 设置mapper文件路径
    if(configuration!=null){
        sessionFactory.setConfiguration(configuration);
    }
    log.info("配置数据源:{} sqlSessionFactory工厂配置结束---------------------------------------",name);

    return sessionFactory.getObject();
}

 

 

好的此时多数据源就配置结束了   -------------------------

根据我们自己配置文件配置的不同数据源的和xml位置 当我们使用dao方法时就会连接对应的数据库 实现多数据源

 

 

什么你以为文章结束了!!!!  no no no   第一种方式简单明了  但是有个致命的问题  就是不够灵活  我们再添加一个数据库 就必须再添加一个配置类  有没有全自动的方式呢?完全根据配置文件来自动生成dataSource sqlSessionFactory 呢?  答案 :有的   下面我们来用高级的方法来实现多数据源配置

 

步骤一

改造刚才的基础配置类  

 BaseSourceConfig
package com.pps.back.frame.pupansheng.core.datasource;

import lombok.extern.slf4j.Slf4j;

/**
 * @author
 * @discription;
 * @time 2020/9/6 12:38
 */
@Slf4j
public class BaseSourceConfig {


    private  String type;

    private   MybatisConfig mybatisConfig=new MybatisConfig();

    private   DataBaseProperty database=new DataBaseProperty();

    private  TransationProperty transationProperty=new TransationProperty();


    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public DataBaseProperty getDatabase() {
        return database;
    }

    public void setDatabase(DataBaseProperty database) {
        this.database = database;
    }

    public TransationProperty getTransationProperty() {
        return transationProperty;
    }

    public void setTransationProperty(TransationProperty transationProperty) {
        this.transationProperty = transationProperty;
    }

    public  static class  TransationProperty{

        private int transactionSynchronization = 0;
        private int defaultTimeout = -1;
        private boolean nestedTransactionAllowed = false;
        private boolean validateExistingTransaction = false;
        private boolean globalRollbackOnParticipationFailure = true;
        private boolean failEarlyOnGlobalRollbackOnly = false;
        private boolean rollbackOnCommitFailure = false;
        private boolean enforceReadOnly;

        public int getTransactionSynchronization() {
            return transactionSynchronization;
        }

        public void setTransactionSynchronization(int transactionSynchronization) {
            this.transactionSynchronization = transactionSynchronization;
        }

        public int getDefaultTimeout() {
            return defaultTimeout;
        }

        public void setDefaultTimeout(int defaultTimeout) {
            this.defaultTimeout = defaultTimeout;
        }

        public boolean isNestedTransactionAllowed() {
            return nestedTransactionAllowed;
        }

        public void setNestedTransactionAllowed(boolean nestedTransactionAllowed) {
            this.nestedTransactionAllowed = nestedTransactionAllowed;
        }

        public boolean isValidateExistingTransaction() {
            return validateExistingTransaction;
        }

        public void setValidateExistingTransaction(boolean validateExistingTransaction) {
            this.validateExistingTransaction = validateExistingTransaction;
        }

        public boolean isGlobalRollbackOnParticipationFailure() {
            return globalRollbackOnParticipationFailure;
        }

        public void setGlobalRollbackOnParticipationFailure(boolean globalRollbackOnParticipationFailure) {
            this.globalRollbackOnParticipationFailure = globalRollbackOnParticipationFailure;
        }

        public boolean isFailEarlyOnGlobalRollbackOnly() {
            return failEarlyOnGlobalRollbackOnly;
        }

        public void setFailEarlyOnGlobalRollbackOnly(boolean failEarlyOnGlobalRollbackOnly) {
            this.failEarlyOnGlobalRollbackOnly = failEarlyOnGlobalRollbackOnly;
        }

        public boolean isRollbackOnCommitFailure() {
            return rollbackOnCommitFailure;
        }

        public void setRollbackOnCommitFailure(boolean rollbackOnCommitFailure) {
            this.rollbackOnCommitFailure = rollbackOnCommitFailure;
        }

        public boolean isEnforceReadOnly() {
            return enforceReadOnly;
        }

        public void setEnforceReadOnly(boolean enforceReadOnly) {
            this.enforceReadOnly = enforceReadOnly;
        }
    }
    public static  class DataBaseProperty{


        private String filters;
        private String url;
        private String username;
        private String password;
        private String driverClassName;
        private int initialSize;
        private int minIdle;
        private int maxActive;
        private long maxWait;
        private long timeBetweenEvictionRunsMillis;
        private long minEvictableIdleTimeMillis;
        private String validationQuery;
        private boolean testWhileIdle;
        private boolean testOnBorrow;
        private boolean testOnReturn;
        private boolean poolPreparedStatements;
        private int maxPoolPreparedStatementPerConnectionSize;

        public String getFilters() {
            return filters;
        }

        public void setFilters(String filters) {
            this.filters = filters;
        }

        public String getUrl() {
            return url;
        }

        public void setUrl(String url) {
            this.url = url;
        }

        public String getUsername() {
            return username;
        }

        public void setUsername(String username) {
            this.username = username;
        }

        public String getPassword() {
            return password;
        }

        public void setPassword(String password) {
            this.password = password;
        }

        public String getDriverClassName() {
            return driverClassName;
        }

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

        public int getInitialSize() {
            return initialSize;
        }

        public void setInitialSize(int initialSize) {
            this.initialSize = initialSize;
        }

        public int getMinIdle() {
            return minIdle;
        }

        public void setMinIdle(int minIdle) {
            this.minIdle = minIdle;
        }

        public int getMaxActive() {
            return maxActive;
        }

        public void setMaxActive(int maxActive) {
            this.maxActive = maxActive;
        }

        public long getMaxWait() {
            return maxWait;
        }

        public void setMaxWait(long maxWait) {
            this.maxWait = maxWait;
        }

        public long getTimeBetweenEvictionRunsMillis() {
            return timeBetweenEvictionRunsMillis;
        }

        public void setTimeBetweenEvictionRunsMillis(long timeBetweenEvictionRunsMillis) {
            this.timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
        }

        public long getMinEvictableIdleTimeMillis() {
            return minEvictableIdleTimeMillis;
        }

        public void setMinEvictableIdleTimeMillis(long minEvictableIdleTimeMillis) {
            this.minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;
        }

        public String getValidationQuery() {
            return validationQuery;
        }

        public void setValidationQuery(String validationQuery) {
            this.validationQuery = validationQuery;
        }

        public boolean isTestWhileIdle() {
            return testWhileIdle;
        }

        public void setTestWhileIdle(boolean testWhileIdle) {
            this.testWhileIdle = testWhileIdle;
        }

        public boolean isTestOnBorrow() {
            return testOnBorrow;
        }

        public void setTestOnBorrow(boolean testOnBorrow) {
            this.testOnBorrow = testOnBorrow;
        }

        public boolean isTestOnReturn() {
            return testOnReturn;
        }

        public void setTestOnReturn(boolean testOnReturn) {
            this.testOnReturn = testOnReturn;
        }

        public boolean isPoolPreparedStatements() {
            return poolPreparedStatements;
        }

        public void setPoolPreparedStatements(boolean poolPreparedStatements) {
            this.poolPreparedStatements = poolPreparedStatements;
        }

        public int getMaxPoolPreparedStatementPerConnectionSize() {
            return maxPoolPreparedStatementPerConnectionSize;
        }

        public void setMaxPoolPreparedStatementPerConnectionSize(int maxPoolPreparedStatementPerConnectionSize) {
            this.maxPoolPreparedStatementPerConnectionSize = maxPoolPreparedStatementPerConnectionSize;
        }
    }

    public MybatisConfig getMybatisConfig() {
        return mybatisConfig;
    }

    public void setMybatisConfig(MybatisConfig mybatisConfig) {
        this.mybatisConfig = mybatisConfig;
    }

    public static  class  MybatisConfig{

        private String [] mapperXmlLocation;

        private  String [] mapperLocation;

        private  String [] typeAliasesPackageList;

        public String[] getMapperXmlLocation() {
            return mapperXmlLocation;
        }

        public void setMapperXmlLocation(String[] mapperXmlLocation) {
            this.mapperXmlLocation = mapperXmlLocation;
        }

        public String[] getMapperLocation() {
            return mapperLocation;
        }

        public void setMapperLocation(String[] mapperLocation) {
            this.mapperLocation = mapperLocation;
        }

        public String[] getTypeAliasesPackageList() {
            return typeAliasesPackageList;
        }

        public void setTypeAliasesPackageList(String[] typeAliasesPackageList) {
            this.typeAliasesPackageList = typeAliasesPackageList;
        }
    }


}

分别封装   数据源属性 和  事务管理属性 和  sqlSessionFactory属性 

步骤二  改变配置文件写法

spring:
  dataSourseList: data-source1        #数据源列表名称 用逗号分割 分别与下面数据源对应  第一个默认为主数据库
  data-source1:
    type: com.alibaba.druid.pool.DruidDataSource  #数据源类型
    database:                                     #数据源基本配置
      username: root
      password: pps123
      driver_class_name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://localhost:3306/webmodel?useUnicode=true&characterEncoding=UTF-8&userSSL=false&serverTimezone=GMT%2B8
    mybatisConfig:
      type-aliases-ackageList:                       #别名

      mapperXmlLocation:                             #mapper.xml  位置
        - classpath:mybatis/security/mapper/*.xml
      mapperLocation:                                #mapper位置
        - com.pps.back.frame.pupansheng.core.authority.security.mapper
        - com.pps.back.frame.pupansheng.mapper

步骤三    实现

ImportBeanDefinitionRegistrar, EnvironmentAware 两个接口  实现通过读取配置文件信息  动态注入我们希望的注入的组件到spring容器中   这一步非常重要  代码很长   但实现的目的很简单  三步

通过配置信息得到一个数据源列表(用逗号分割) 然后分割   通过前缀获取到具体每一个数据源的属性

对于每一个具体的数据源

首先往容器中添加数据源

然后往容器中添加事务管理器

再然后往容器中添加sqlSessionFactory工厂

结束

其实就是第一种方式自动化了

package com.pps.back.frame.pupansheng.core.datasource;

import com.pps.back.frame.pupansheng.common.util.ValidateUtil;
import com.zaxxer.hikari.HikariDataSource;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.mapping.DatabaseIdProvider;
import org.apache.ibatis.mapping.VendorDatabaseIdProvider;
import org.apache.ibatis.type.TypeHandler;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.mapper.ClassPathMapperScanner;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.boot.context.properties.bind.BindResult;
import org.springframework.boot.context.properties.bind.Bindable;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author
 * @discription;
 * @time 2020/9/11 21:50
 */
@Slf4j
public class MyBatisDataSourceProcessor implements ImportBeanDefinitionRegistrar, EnvironmentAware {

    private Environment environment;

    private final String DATASOURCE_DATA_LIST = "spring.dataSourseList";
    private final String DATASOURCE_PREFIX = "spring";
    private final String SPILIT = ",";
    private final String ADD_CHAR = ".";
    private Binder binder;

    @SneakyThrows
    @Override
    public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
        binder = Binder.get(this.getEnvironment());
        String dataSourceList = environment.getProperty(DATASOURCE_DATA_LIST);

        if (ValidateUtil.isNotEmpty(dataSourceList)) {
            log.info("数据源配置开始---------------------------------------------------------------------------------------");
            String[] dataSourses = dataSourceList.split(SPILIT);
            boolean isFirst=true;
            for (String dataSourseName : dataSourses) {

                BindResult<BaseSourceConfig> bind = binder.bind(DATASOURCE_PREFIX + ADD_CHAR + dataSourseName, BaseSourceConfig.class);
                BaseSourceConfig baseSourceConfig = bind.get();
                log.info("数据源:{} 自动配置 开始------------------------------------------------",dataSourseName);
                log.info("为数据源:{}配置数据源--------------------------------",dataSourseName);
                //配置数据源
                {

                    Map dataSourceProperties= (Map) this.binder.bind(DATASOURCE_PREFIX + ADD_CHAR + dataSourseName+".database", Bindable.of(Map.class)).get();

                    Class<?> aClass = null;
                    if(ValidateUtil.isEmpty(baseSourceConfig.getType())||(aClass= Class.forName(baseSourceConfig.getType()))==null){
                        log.warn("指定的数据源类型为空或类库不存在 !!!应用采用默认的数据库源:{}数据源"," HikariDataSource.class");
                        aClass= HikariDataSource.class;
                    }
                    Map map = validateConfigMap(aClass, dataSourceProperties);
                    registerBean(beanDefinitionRegistry, dataSourseName,map, aClass,isFirst);
                }
                log.info("为数据源:{}配置数据源:{}  结束---------------------------",dataSourseName,dataSourseName);
                log.info("为数据源:{}配置事务管理器-----------------------------",dataSourseName);
                //配置事务管理器
                {
                    Class tr= DataSourceTransactionManager.class;
                    log.warn("---采用的事务处理管理器为:{}",tr.getName());
                    registerBean(beanDefinitionRegistry, dataSourseName + "TransactionManager", this.buildTransactionManager(DATASOURCE_PREFIX, dataSourseName,tr), tr,isFirst);
                }
                log.info("为数据源:{}配置事务管理器 :{}  结束--------------------------",dataSourseName,dataSourseName + "TransactionManager");
                log.info("为数据源:{} 配置SqlSessionFactory工厂:{}--------------------------------",dataSourseName,dataSourseName + "SqlSessionFactory");
                //配置sessiong工厂
                {

                    registerBean(beanDefinitionRegistry, dataSourseName + "SqlSessionFactory", this.buildSqlSessionFactoryBean(binder, dataSourseName, baseSourceConfig), SqlSessionFactoryBean.class,isFirst);

                }
                log.info("为数据源:{} 配置SqlSessionFactory工厂:{}---------结束---------------------",dataSourseName,dataSourseName + "SqlSessionFactory");
                //注册mapper扫描
                log.info("为数据源:{} 配置mapper位置 >>>>>>>>--------------------------------",dataSourseName);
                {
                    registerMapper(beanDefinitionRegistry, baseSourceConfig, dataSourseName,dataSourseName + "SqlSessionFactory");
                }
                log.info("为数据源:{} 配置mapper位置 结束>>>>>>>>--------------------------------",dataSourseName);
                log.info("数据源:{} 自动配置 结束------------------------------------------------",dataSourseName);
                if(isFirst) {
                    isFirst = false;
                }
            }

        } else {
            log.warn("未检查到数据源名称 无法启动数据源自动配置");
        }

        log.info("数据源配置结束---------------------------------------------------------------------------------------");


    }

    @Override
    public void setEnvironment(Environment environment) {

        this.environment = environment;
    }

    public Environment getEnvironment() {
        return environment;
    }

    private void registerMapper(BeanDefinitionRegistry beanDefinitionRegistry, BaseSourceConfig baseSourceConfig, String dataSourseName,String sessionFactoryName) {

        String[] mappersList = baseSourceConfig.getMybatisConfig().getMapperLocation();
        if (ValidateUtil.isEmpty(mappersList)) {
            throw new RuntimeException("对于数据源:" + dataSourseName + ",指定的mapper包名  为空");
        }
        log.info("配置数据源:{}  mappper  位置-------------------------------------------------------", dataSourseName);
        for (String mapperPackage : mappersList) {
            log.info("为数据源:{}  配置dao接口 mapper 位置:{}", dataSourseName, mapperPackage);
            ClassPathMapperScanner scanner = new ClassPathMapperScanner(beanDefinitionRegistry);
            scanner.setSqlSessionFactoryBeanName(sessionFactoryName);
            scanner.registerFilters();
            scanner.doScan(mapperPackage);

        }
        log.info("配置数据源:{}  mappper  位置结束-----------------------------------------------------", dataSourseName);
    }

    private void registerBean(BeanDefinitionRegistry registry, String beanName, Map<String, Object> attributeMap, Class<?> clazz,Boolean primary) {
        BeanDefinitionBuilder dbBuilder = BeanDefinitionBuilder.rootBeanDefinition(clazz);
        RootBeanDefinition dbBeanDefinition = (RootBeanDefinition) dbBuilder.getBeanDefinition();
        dbBeanDefinition.getPropertyValues().addPropertyValues(attributeMap);
        dbBeanDefinition.setPrimary(primary);
        registry.registerBeanDefinition(beanName, dbBeanDefinition);
    }

    protected Map<String, Object> buildTransactionManager(String prefix, String beanName,Class tr) {
        Map<String, Object> dstmMap = new HashMap(5);
        dstmMap.put("dataSource", new RuntimeBeanReference(beanName));
        Map<String, Object> map = this.extendAttributes(prefix + "." + beanName + ".transaction");
        if(map!=null) {
            dstmMap.putAll(validateConfigMap(tr,map));
        }
        return dstmMap;
    }

    protected Map<String, Object> extendAttributes(String path) {
        Binder binder = Binder.get(this.environment);

        try {
            return (Map) binder.bind(path, Bindable.of(HashMap.class)).get();
        } catch (Exception var4) {
            return null;
        }
    }

    private Map<String, Object> buildSqlSessionFactoryBean(Binder binder, String beanName, BaseSourceConfig baseSourceConfig) {
        Map<String, Object> sqlMap = new HashMap(5);

        class Util {

            protected String cutEndChar(List<String> list) {
                StringBuilder sb = new StringBuilder();
                list.stream().forEach((x) -> {
                    sb.append(x).append(",");
                });
                return sb.toString().substring(0, sb.length() - 1);
            }

            private DatabaseIdProvider buildDatabaseIdProvider() {
                DatabaseIdProvider databaseIdProvider = new VendorDatabaseIdProvider();
                Properties properties = new Properties();
                Arrays.stream(DriverType.values()).forEach((driverType) -> {
                    properties.setProperty(driverType.getPattern(), driverType.getType());
                });
                databaseIdProvider.setProperties(properties);
                return databaseIdProvider;
            }

            private TypeHandler[] buildTypeHandle() {
                TypeHandler[] typeHandlers = new TypeHandler[]{new LvarStringTypeHandler()};
                return typeHandlers;
            }

        }
        Util util = new Util();
        sqlMap.put("dataSource", new RuntimeBeanReference(beanName));
        List<String> typeAliasesPackageList =null;

        if (StringUtils.isEmpty(baseSourceConfig.getMybatisConfig().getTypeAliasesPackageList())) {
            log.warn("警告:数据源:{}-----------type-aliases-package属性为空",beanName);
        }else {
            typeAliasesPackageList= Arrays.asList(baseSourceConfig.getMybatisConfig().getTypeAliasesPackageList());
        }
        if (StringUtils.isEmpty(baseSourceConfig.getMybatisConfig().getMapperXmlLocation())) {
            throw new RuntimeException("mapper-xml-location属性为空");
        }

        List<String> mapperLocationsList = Arrays.asList(baseSourceConfig.getMybatisConfig().getMapperXmlLocation());

        if (!ValidateUtil.isEmpty(typeAliasesPackageList)) {
            sqlMap.put("typeAliasesPackage", util.cutEndChar(typeAliasesPackageList));
        }
        sqlMap.put("mapperLocations", mapperLocationsList);
        sqlMap.put("databaseIdProvider", util.buildDatabaseIdProvider());
        sqlMap.put("typeHandlers", util.buildTypeHandle());
        Map<String, Object> extendMap = this.extendAttributes(DATASOURCE_PREFIX + ADD_CHAR + beanName + ".sqlsessionfactory");
        if (extendMap != null) {
            sqlMap.putAll(extendMap);
        }
        return sqlMap;
    }

    protected Map<String, Object> validateConfigMap(Class obj, Map<String, Object> map) {
        Map<String, Object> newMap = new HashMap(10);
        map.keySet().stream().forEach((key) -> {

            String methodName = "get";

            //驼峰转换
            if(key.contains("_")){
                key=  Arrays.stream(key.split("_")).map(s->s.substring(0, 1).toUpperCase().concat(s.substring(1))).collect(Collectors.joining());
            }
            key=key.substring(0, 1).toLowerCase().concat(key.substring(1));
            methodName = methodName + key.substring(0, 1).toUpperCase().concat(key.substring(1));
            try {
                obj.getMethod(methodName);
                newMap.put(key, map.get(key));
            } catch (Exception var7) {

                log.warn("{} 属性:  {}不存在  这个配置不合理",obj.getName(), key);
            }

        });
        return newMap;
    }

}





补上其他两个辅助类:

public enum DriverType {
    MYSQL("MySQL", "MYSQL"),
    ORACLE("Oracle", "ORACLE"),
    SQLSERVER("SQL Server", "SQLSERVER"),
    INFORMIX("Informix", "INFORMIX"),
    H2("H2", "H2"),
    POSTGRESQL("PostgreSQL", "POSTGRESQL");

    private String pattern;
    private String type;

    private DriverType(String pattern, String type) {
        this.pattern = pattern;
        this.type = type;
    }

    public String getPattern() {
        return this.pattern;
    }

    public String getType() {
        return this.type;
    }

    @Override
    public String toString() {
        return this.type;
    }
}
@MappedJdbcTypes({JdbcType.LONGVARCHAR})
@MappedTypes({String.class})
public class LvarStringTypeHandler extends BaseTypeHandler<String> {
    public LvarStringTypeHandler() {
    }

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
        ps.setString(i, parameter);
    }

    @Override
    public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
        return rs.getString(columnName);
    }

    @Override
    public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        return rs.getString(columnIndex);
    }

    @Override
    public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        return cs.getString(columnIndex);
    }
}

 

步骤四   在启动类加上 

@Import(MyBatisDataSourceProcessor.class)

 

ok  完成

 

附上 应用启动的日志记录

 

感谢观看!  

 

 

 

 

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot应用中使用Mybatis和Hikari实现多数据源,可以按照以下步骤进行操作: 1. 在pom.xml文件中添加相关依赖,包括spring-boot-starter-jdbc、mybatis-spring-boot-starter、hikariCP等。 2. 在application.yml配置文件中配置多个数据源,例如: ``` spring: datasource: master: jdbc-url: jdbc:mysql://localhost:3306/master username: root password: root driver-class-name: com.mysql.jdbc.Driver slave: jdbc-url: jdbc:mysql://localhost:3306/slave username: root password: root driver-class-name: com.mysql.jdbc.Driver ``` 3. 创建多个数据源的配置类,例如: ``` @Configuration @MapperScan(basePackages = "com.example.demo.master.mapper", sqlSessionTemplateRef = "masterSqlSessionTemplate") public class MasterDataSourceConfig { @Primary @Bean(name = "masterDataSource") @ConfigurationProperties(prefix = "spring.datasource.master") public DataSource masterDataSource() { return DataSourceBuilder.create().type(HikariDataSource.class).build(); } @Primary @Bean(name = "masterSqlSessionFactory") public SqlSessionFactory masterSqlSessionFactory(@Qualifier("masterDataSource") DataSource dataSource) throws Exception { SqlSessionFactoryBean bean = new SqlSessionFactoryBean(); bean.setDataSource(dataSource); return bean.getObject(); } @Primary @Bean(name = "masterTransactionManager") public DataSourceTransactionManager masterTransactionManager(@Qualifier("masterDataSource") DataSource dataSource) { return new DataSourceTransactionManager(dataSource); } @Primary @Bean(name = "masterSqlSessionTemplate") public SqlSessionTemplate masterSqlSessionTemplate(@Qualifier("masterSqlSessionFactory") SqlSessionFactory sqlSessionFactory) { return new SqlSessionTemplate(sqlSessionFactory); } } ``` ``` @Configuration @MapperScan(basePackages = "com.example.demo.slave.mapper", sqlSessionTemplateRef = "slaveSqlSessionTemplate") public class SlaveDataSourceConfig { @Bean(name = "slaveDataSource") @ConfigurationProperties(prefix = "spring.datasource.slave") public DataSource slaveDataSource() { return DataSourceBuilder.create().type(HikariDataSource.class).build(); } @Bean(name = "slaveSqlSessionFactory") public SqlSessionFactory slaveSqlSessionFactory(@Qualifier("slaveDataSource") DataSource dataSource) throws Exception { SqlSessionFactoryBean bean = new SqlSessionFactoryBean(); bean.setDataSource(dataSource); return bean.getObject(); } @Bean(name = "slaveTransactionManager") public DataSourceTransactionManager slaveTransactionManager(@Qualifier("slaveDataSource") DataSource dataSource) { return new DataSourceTransactionManager(dataSource); } @Bean(name = "slaveSqlSessionTemplate") public SqlSessionTemplate slaveSqlSessionTemplate(@Qualifier("slaveSqlSessionFactory") SqlSessionFactory sqlSessionFactory) { return new SqlSessionTemplate(sqlSessionFactory); } } ``` 4. 在需要使用数据源的地方使用@Qualifier注解指定数据源,例如: ``` @Service public class UserService { @Autowired @Qualifier("masterSqlSessionTemplate") private SqlSessionTemplate masterSqlSessionTemplate; @Autowired @Qualifier("slaveSqlSessionTemplate") private SqlSessionTemplate slaveSqlSessionTemplate; public List<User> getUsersFromMaster() { return masterSqlSessionTemplate.selectList("com.example.demo.master.mapper.UserMapper.selectAll"); } public List<User> getUsersFromSlave() { return slaveSqlSessionTemplate.selectList("com.example.demo.slave.mapper.UserMapper.selectAll"); } } ``` 这样就可以在Spring Boot应用中使用Mybatis和Hikari实现多数据源了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值