springboot mybatis 多数据源配置(笔记)

一、多config文件形式

1、yml文件配置

(1)master / slave

spring:
  datasource:
    dynamic:
      datasource:
        master:
          name: xxx
          url: jdbc:dm://localhost:8001/xxx?characterEncoding=UTF-8&useUnicode=true&useSSL=false
          username: xxx
          password: xxx
          driver-class-name: dm.jdbc.driver.DmDriver
        slave1:
          name: xxx
          url: jdbc:dm://localhost:8001/xxx?characterEncoding=UTF-8&useUnicode=true&useSSL=false
          username: xxx
          password: xxx
          driver-class-name: dm.jdbc.driver.DmDriver
          max-active: 20

(2)数组

spring:
  datasource:
    datasource-configs[0]:
      pool-name: flowdatabase(流程数据源)
      jdbc-url: jdbc:mysql://localhost:3306/xxx?useUnicode=true&characterEncoding=utf-8&useSSL=false&nullCatalogMeansCurrent=true&serverTimezone=GMT%2B8
      username: xxx
      password: xxx
      driver-class-name: com.mysql.cj.jdbc.Driver
      maximum-pool-size: 50
    datasource-configs[1]:
      pool-name: bizdatabase(业务数据源)
      jdbc-url: jdbc:mysql://localhost:3306/xxx?useUnicode=true&characterEncoding=utf-8&useSSL=false&nullCatalogMeansCurrent=true&serverTimezone=GMT%2B8
      username: xxx
      password: xxx
      driver-class-name: com.mysql.cj.jdbc.Driver

2、config文件

(1)主数据源(默认)

//表示这个类为一个配置类
@Configuration
// 配置mybatis的接口类放的地方
@MapperScan(basePackages = "com.mzd.multipledatasources.mapper.test01", sqlSessionFactoryRef = "test1SqlSessionFactory")
public class DataSourceConfig1 {
    // 将这个对象放入Spring容器中
    @Bean(name = "test1DataSource")
    // 表示这个数据源是默认数据源
    @Primary
    // 读取application.properties中的配置参数映射成为一个对象
    // prefix表示参数的前缀
    @ConfigurationProperties(prefix = "spring.datasource.test1")
    public DataSource getDateSource1() {
        return DataSourceBuilder.create().build();
    }
    @Bean(name = "test1SqlSessionFactory")
    // 表示这个数据源是默认数据源
    @Primary
    // @Qualifier表示查找Spring容器中名字为test1DataSource的对象
    public SqlSessionFactory test1SqlSessionFactory(@Qualifier("test1DataSource") DataSource datasource)
            throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(datasource);
        bean.setMapperLocations(
                // 设置mybatis的xml所在位置
                new PathMatchingResourcePatternResolver().getResources("classpath*:mapping/test01/*.xml"));
        return bean.getObject();
    }
    @Bean("test1SqlSessionTemplate")
    // 表示这个数据源是默认数据源
    @Primary
    public SqlSessionTemplate test1sqlsessiontemplate(
            @Qualifier("test1SqlSessionFactory") SqlSessionFactory sessionfactory) {
        return new SqlSessionTemplate(sessionfactory);
    }
}

(2)从数据源

@Configuration
@MapperScan(basePackages = "com.mzd.multipledatasources.mapper.test02", sqlSessionFactoryRef = "test2SqlSessionFactory")
public class DataSourceConfig2 {
    @Bean(name = "test2DataSource")
    @ConfigurationProperties(prefix = "spring.datasource.test2")
    public DataSource getDateSource2() {
        return DataSourceBuilder.create().build();
    }
    @Bean(name = "test2SqlSessionFactory")
    public SqlSessionFactory test2SqlSessionFactory(@Qualifier("test2DataSource") DataSource datasource)
            throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(datasource);
        bean.setMapperLocations(
                new PathMatchingResourcePatternResolver().getResources("classpath*:mapping/test02/*.xml"));
        return bean.getObject();
    }
    @Bean("test2SqlSessionTemplate")
    public SqlSessionTemplate test2sqlsessiontemplate(
            @Qualifier("test2SqlSessionFactory") SqlSessionFactory sessionfactory) {
        return new SqlSessionTemplate(sessionfactory);
    }
}

3、注意事项

1、必须添加@Primary注解,保证mybatis能够找到哪一个是默认的数据源

2、mapper接口以及xml文件都要放到不同的包下(dao层要进行区分)

3、bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(“XXXX”));mapper的xml形式文件位置必须要配置,不然将报错:no statement (这种错误也可能是mapper的xml中,namespace与项目的路径不一致导致的,具体看情况吧,注意一下就行,问题不大的)

二、AOP实现

1、什么是AbstractRoutingDataSource

​ 实质:有多少个数据源就存多少个数据源在targetDataSources(是AbstractRoutingDataSource的一个map类型的属性,其中value为每个数据源,key表示每个数据源的名字)这个属性中,然后根据determineCurrentLookupKey()这个方法获取当前数据源在map中的key值,然后determineTargetDataSource()方法中动态获取当前数据源,如果当前数据源不存并且默认数据源也不存在就抛出异常。

public abstract class AbstractRoutingDataSource extends AbstractDataSource implements InitializingBean {
    //多数据源map集合
    private Map<Object, Object> targetDataSources;
    //默认数据源
    private Object defaultTargetDataSource;
    //其实就是targetDataSources,后面的afterPropertiesSet()方法会将targetDataSources赋值给resolvedDataSources
    private Map<Object, DataSource> resolvedDataSources;
    private DataSource resolvedDefaultDataSource;
    public void setTargetDataSources(Map<Object, Object> targetDataSources) {
        this.targetDataSources = targetDataSources;
    }
    protected DataSource determineTargetDataSource() {
        Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");
        Object lookupKey = this.determineCurrentLookupKey();
        DataSource dataSource = (DataSource)this.resolvedDataSources.get(lookupKey);
        if (dataSource == null && (this.lenientFallback || lookupKey == null)) {
            dataSource = this.resolvedDefaultDataSource;
        }
        if (dataSource == null) {
            throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");
        } else {
            return dataSource;
        }
    }
    protected abstract Object determineCurrentLookupKey();
}

2、如何通过AOP实现多数据源切换(配置)

(1)定义动态数据源

继承AbstractRoutingDataSource 抽象类,并重写determineCurrentLookupKey()方法

public class DynamicDataSource extends AbstractRoutingDataSource {

    @Override
    protected Object determineCurrentLookupKey() {
        DataSourceType.DataBaseType dataBaseType = DataSourceType.getDataBaseType();
        return dataBaseType;
    }
    
}

(2)创建一个切换数据源类型的类

public class DataSourceType {

    public enum DataBaseType {
        TEST01, TEST02
    }

    // 使用ThreadLocal保证线程安全
    private static final ThreadLocal<DataBaseType> TYPE = new ThreadLocal<DataBaseType>();

    // 往当前线程里设置数据源类型
    public static void setDataBaseType(DataBaseType dataBaseType) {
        if (dataBaseType == null) {
            throw new NullPointerException();
        }
        System.err.println("[将当前数据源改为]:" + dataBaseType);
        TYPE.set(dataBaseType);
    }

    // 获取数据源类型
    public static DataBaseType getDataBaseType() {
        DataBaseType dataBaseType = TYPE.get() == null ? DataBaseType.TEST01 : TYPE.get();
        System.err.println("[获取当前数据源的类型为]:" + dataBaseType);
        return dataBaseType;
    }

    // 清空数据类型
    public static void clearDataBaseType() {
        TYPE.remove();
    }

}

(3)定义多个数据源

将定义好的多个数据源放在动态数据源中

@Configuration
@MapperScan(basePackages = "com.mzd.multipledatasources.mapper", sqlSessionFactoryRef = "SqlSessionFactory")
public class DataSourceConfig {
    @Primary
    @Bean(name = "test1DataSource")
    @ConfigurationProperties(prefix = "spring.datasource.test1")
    public DataSource getDateSource1() {
        return DataSourceBuilder.create().build();
    }
    @Bean(name = "test2DataSource")
    @ConfigurationProperties(prefix = "spring.datasource.test2")
    public DataSource getDateSource2() {
        return DataSourceBuilder.create().build();
    }

    @Bean(name = "dynamicDataSource")
    public DynamicDataSource DataSource(@Qualifier("test1DataSource") DataSource test1DataSource,
            @Qualifier("test2DataSource") DataSource test2DataSource) {
        Map<Object, Object> targetDataSource = new HashMap<>();
        targetDataSource.put(DataSourceType.DataBaseType.TEST01, test1DataSource);
        targetDataSource.put(DataSourceType.DataBaseType.TEST02, test2DataSource);
        DynamicDataSource dataSource = new DynamicDataSource();
        dataSource.setTargetDataSources(targetDataSource);
        dataSource.setDefaultTargetDataSource(test1DataSource);
        return dataSource;
    }
    @Bean(name = "SqlSessionFactory")
    public SqlSessionFactory test1SqlSessionFactory(@Qualifier("dynamicDataSource") DataSource dynamicDataSource)
            throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dynamicDataSource);
        bean.setMapperLocations(
                new PathMatchingResourcePatternResolver().getResources("classpath*:mapping/*.xml"));
        return bean.getObject();
    }
}

(4)定义AOP

不同业务切换不同数据库的入口

也可以通过注解的形式进行实现

@Aspect
@Component
public class DataSourceAop {
    @Before("execution(* com.mzd.multipledatasources.service..*.test01*(..))")
    public void setDataSource2test01() {
        System.err.println("test01业务");
        DataSourceType.setDataBaseType(DataBaseType.TEST01);
    }

    @Before("execution(* com.mzd.multipledatasources.service..*.test02*(..))")
    public void setDataSource2test02() {
        System.err.println("test02业务");
        DataSourceType.setDataBaseType(DataBaseType.TEST02);
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值