SpringBoot/Mybatis/Druid, 多数据源MultiDataSource配置思路

新的项目需求,需要将子需求A锁定到A库,将子需求B锁定到B库;
就是俗称的“分库”;但这是业务性分库,也就是说,A库和B库并没有任何逻辑上的,主从或水平或垂直分库关系;就是,完全没有关系

思路:

  1. SpringBoot可配置多数据源,但项目中使用Druid进行数据源管理;
  2. 配置好数据源DataSource后,需要将DataSource交给Mybatis的SqlSessionFactory建立相关连接;
  3. 上述两个配置需要结合SpringBoot的@Configuration注解使用,方便简洁;
  4. 多说一句, 整体思路就这么简单, 但细节的修正与项目息息相关, 请各位耐心调试;

实施:

1. yml文件
spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    master:
      driverClassName: com.mysql.jdbc.Driver
      url: jdbc:mysql://aaa
      username: xxxaaa
      password: xxxaaa
    report:
      driverClassName: com.mysql.jdbc.Driver
      url: jdbc:mysql://bbb
      username: xxxbbb
      password: xxxbbb
    initialSize: 1
    minIdle: 3
    maxActive: 20
    # 配置获取连接等待超时的时间
    maxWait: 60000
    # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
    timeBetweenEvictionRunsMillis: 60000
    # 配置一个连接在池中最小生存的时间,单位是毫秒
    minEvictableIdleTimeMillis: 30000
    validationQuery: select 'x'
    testWhileIdle: true
    testOnBorrow: false
    testOnReturn: false
    # 打开PSCache,并且指定每个连接上PSCache的大小
    poolPreparedStatements: true
    maxPoolPreparedStatementPerConnectionSize: 20
    # 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
    filters: stat,wall,slf4j
    # 通过connectProperties属性来打开mergeSql功能;慢SQL记录
    connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
    # 合并多个DruidDataSource的监控数据
    #useGlobalDataSourceStat: true
2. dataSource-aaa:
package com.project.common.config.datasource;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;

import javax.sql.DataSource;
import java.sql.SQLException;

@SuppressWarnings("AlibabaRemoveCommentedCode")
@MapperScan(basePackages = DruidDBConfig.daoPackages, sqlSessionTemplateRef = "aaaSqlSessionTemplate")
@Configuration
public class DruidDBConfig {

    public static final String domainPackages = "com.project.**.domain";
    public static final String daoPackages = "com.project.*.dao";
    public static final String mapperLocations = "classpath:mybatis/aaa/**/*Mapper.xml";

    private Logger logger = LoggerFactory.getLogger(DruidDBConfig.class);
    @Value("${spring.datasource.aaa.url}")
    private String dbUrl;

    @Value("${spring.datasource.aaa.username}")
    private String username;

    @Value("${spring.datasource.aaa.password}")
    private String password;

    @Value("${spring.datasource.aaa.driverClassName}")
    private String driverClassName;

    @Value("${spring.datasource.initialSize}")
    private int initialSize;

    @Value("${spring.datasource.minIdle}")
    private int minIdle;

    @Value("${spring.datasource.maxActive}")
    private int maxActive;

    @Value("${spring.datasource.maxWait}")
    private int maxWait;

    @Value("${spring.datasource.timeBetweenEvictionRunsMillis}")
    private int timeBetweenEvictionRunsMillis;

    @Value("${spring.datasource.minEvictableIdleTimeMillis}")
    private int minEvictableIdleTimeMillis;

    @Value("${spring.datasource.validationQuery}")
    private String validationQuery;

    @Value("${spring.datasource.testWhileIdle}")
    private boolean testWhileIdle;

    @Value("${spring.datasource.testOnBorrow}")
    private boolean testOnBorrow;

    @Value("${spring.datasource.testOnReturn}")
    private boolean testOnReturn;

    @Value("${spring.datasource.poolPreparedStatements}")
    private boolean poolPreparedStatements;

    @Value("${spring.datasource.maxPoolPreparedStatementPerConnectionSize}")
    private int maxPoolPreparedStatementPerConnectionSize;

    @Value("${spring.datasource.filters}")
    private String filters;

    @Value("{spring.datasource.connectionProperties}")
    private String connectionProperties;

    @Bean(name = "aaaDataSource", initMethod = "init", destroyMethod = "close")   //声明其为Bean实例
    @Primary  //在同样的DataSource中,首先使用被标注的DataSource
    public DataSource aaaDataSource() {
        DruidDataSource datasource = new DruidDataSource();

        datasource.setUrl(this.dbUrl);
        datasource.setUsername(username);
        datasource.setPassword(password);
        datasource.setDriverClassName(driverClassName);

        //configuration
        datasource.setInitialSize(initialSize);
        datasource.setMinIdle(minIdle);
        datasource.setMaxActive(maxActive);
        datasource.setMaxWait(maxWait);
        datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
        datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
        datasource.setValidationQuery(validationQuery);
        datasource.setTestWhileIdle(testWhileIdle);
        datasource.setTestOnBorrow(testOnBorrow);
        datasource.setTestOnReturn(testOnReturn);
        datasource.setPoolPreparedStatements(poolPreparedStatements);
        datasource.setMaxPoolPreparedStatementPerConnectionSize(maxPoolPreparedStatementPerConnectionSize);
        try {
            datasource.setFilters(filters);
        } catch (SQLException e) {
            logger.error("druid configuration initialization filter", e);
        }
        datasource.setConnectionProperties(connectionProperties);

        return datasource;
    }

    @Bean(name = "aaaSqlSessionFactory")
    @Primary
    public SqlSessionFactory aaaSqlSessionFactory(@Qualifier("aaaDataSource") DataSource aaaDataSource) throws Exception {
        /*
        **!: SqlSessionFactory的配置是重点,详细定制Mybatis针对不同数据源的
            配置信息,下面几行代码分别配置:
              1. Entity包名
              2. DB字段名下划线, 映射成, 驼峰命名法
              3. Mybatis, Mapper的包所在位置
        */
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(aaaDataSource);
        // 扫描Entity
        bean.setTypeAliasesPackage(domainPackages);

        org.apache.ibatis.session.Configuration conf = new org.apache.ibatis.session.Configuration();
        conf.setMapUnderscoreToCamelCase(true);
        bean.setConfiguration(conf);

        bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(mapperLocations));
        return bean.getObject();
    }

    @Bean(name = "aaaTransactionManager")
    @Primary
    public DataSourceTransactionManager aaaTransactionManager(@Qualifier("aaaDataSource") DataSource aaaDataSource) {
        return new DataSourceTransactionManager(aaaDataSource);
    }

    @Bean(name = "aaaSqlSessionTemplate")
    @Primary
    public SqlSessionTemplate aaaSqlSessionTemplate(@Qualifier("aaaSqlSessionFactory") SqlSessionFactory aaaSqlSessionFactory) throws Exception {
        return new SqlSessionTemplate(aaaSqlSessionFactory);
    }
}
dataSource-bbb:

bbbaaa配置完全雷同, 可以在上述重点处调整针对不同数据源的SqlSessionFactory细节;

细节与坑:

public static final String domainPackages = "com.project.**.domain";
public static final String daoPackages = "com.project.*.dao";
public static final String mapperLocations = "classpath:mybatis/aaa/**/*Mapper.xml";

mybatis相关的, Entity, Dao, Xml三层, 不同数据源的文件最好放到不同的包里, 做好资源隔离, 相信在调试过程中你会遇到很多资源冲突的bug, 祝好运~

猜你喜欢

转载自blog.csdn.net/u014535970/article/details/81127757