springboot+druid+mybatis plus的多數據源配置


記得面試時候,有面試官會問道,你們多數據源是怎么實現的呀。.......,一陣蒙蔽中,然后說道我們之前項目中,沒有用到多數據源。

所幸,目前做得項目中有一個業務邏輯中,用到多個數據庫數據情況,多數據源華麗上線。

一. mybatis plus

      因為我們項目是springboot+mybatis plus,有些人一看,mybatis還知道對吧,mybatis plus是什么鬼,其實字面意思可以理解,就是對mybatis進行一些功能改造,一些封裝升級,然后用起來特別方便。

     核心功能的升級主要是以下三點:

     支持通用的 CRUD、代碼生成器與條件構造器。

      通用 CRUD:定義好 Mapper 接口后,只需要繼承 BaseMapper<T> 接口即可獲得通用的增刪改查功能,無需編寫任何接口方法與配置文件
     條件構造器:通過 EntityWrapper<T> (實體包裝類),可以用於拼接 SQL 語句,並且支持排序、分組查詢等復雜的 SQL
     代碼生成器:支持一系列的策略配置與全局配置,比 MyBatis 的代碼生成更好用

二.多數據源配置開始

    思路:

  1、yml中配置多個數據源信息

  2、通過AOP切換不同數據源

  3、配合mybatis plus使用

     

1、yml配置

spring:
  aop:
      proxy-target-class: true
      auto: true
  datasource:
    druid:
      db1:
        url: jdbc:mysql://localhost:3306/eboot
        username: root
        password: root
        driver-class-name: com.mysql.jdbc.Driver
        initialSize: 5
        minIdle: 5
        maxActive: 20
      db2:
        url: jdbc:oracle:thin:@192.168.136.222:ORCL
        username: sa
        password: sa123456
        driver-class-name: oracle.jdbc.OracleDriver
        initialSize: 5
        minIdle: 5
        maxActive: 20

  



2、啟動多個數據源

@EnableTransactionManagement //開啟事務
@Configuration  //spring中常用到注解,與xml配置相對立。是兩種加載bean方式
@MapperScan("com.df.openapi.**.mapper.db*") // 掃描mapperdao的地址
public class MybatisPlusConfig {

    @Bean
    public PaginationInterceptor paginationInterceptor() {
        PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
//        paginationInterceptor.setLocalPage(true); // 由於版本問題,有些類可能招不到這個方法,需要升級jar包
        return paginationInterceptor;
    }

    @Bean(name = "db1")
    @ConfigurationProperties(prefix = "spring.datasource.druid.db1")
    public DataSource db1() {
        return DruidDataSourceBuilder.create().build();
    }

    @Bean(name = "db2")
    @ConfigurationProperties(prefix = "spring.datasource.druid.db2")
    public DataSource db2() {
        return DruidDataSourceBuilder.create().build();
    }

    /**
     * 動態數據源配置
     *
     * @return
     */
    @Bean
    @Primary
    public DataSource multipleDataSource(@Qualifier("db1") DataSource db1,
                                         @Qualifier("db2") DataSource db2) {
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put(DBTypeEnum.db1.getValue(), db1);
        targetDataSources.put(DBTypeEnum.db2.getValue(), db2);
        dynamicDataSource.setTargetDataSources(targetDataSources);
        dynamicDataSource.setDefaultTargetDataSource(db2); // 程序默認數據源,這個要根據程序調用數據源頻次,經常把常調用的數據源作為默認
        return dynamicDataSource;
    }

    @Bean("sqlSessionFactory")
    public SqlSessionFactory sqlSessionFactory() throws Exception {
        MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean();
        sqlSessionFactory.setDataSource(multipleDataSource(db1(), db2()));

        MybatisConfiguration configuration = new MybatisConfiguration();
        configuration.setJdbcTypeForNull(JdbcType.NULL);
        configuration.setMapUnderscoreToCamelCase(true);
        configuration.setCacheEnabled(false);
        sqlSessionFactory.setConfiguration(configuration);
        //PerformanceInterceptor(),OptimisticLockerInterceptor()
        //添加分頁功能
        sqlSessionFactory.setPlugins(new Interceptor[]{
                paginationInterceptor()
        });
//        sqlSessionFactory.setGlobalConfig(globalConfiguration()); //注釋掉全局配置,因為在xml中讀取就是全局配置
        return sqlSessionFactory.getObject();
    }

 /*   @Bean
    public GlobalConfiguration globalConfiguration() {
        GlobalConfiguration conf = new GlobalConfiguration(new LogicSqlInjector());
        conf.setLogicDeleteValue("-1");
        conf.setLogicNotDeleteValue("1");
        conf.setIdType(0);
        conf.setMetaObjectHandler(new MyMetaObjectHandler());
        conf.setDbColumnUnderline(true);
        conf.setRefresh(true);
        return conf;
    }*/
}

  

3、DBType枚舉類

package com.df.openapi.config.db;

public enum DBTypeEnum {

    db1("db1"), db2("db2"); 
    private String value;

    DBTypeEnum(String value) {
        this.value = value;
    }

    public String getValue() {
        return value;
    }
}

  

4、動態數據源決策

package com.df.openapi.config.db;

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

public class DynamicDataSource extends AbstractRoutingDataSource {
private static final Logger LOGGER = LoggerFactory.getLogger(DynamicDataSource.class);

@Override
protected Object determineCurrentLookupKey() {
String datasource = DataSourceContextHolder.getDbType();
LOGGER.debug("使用數據源 {}", datasource);
return datasource;
}
}

  

5、設置、獲取數據源

public class DataSourceContextHolder {

    private static final Logger LOGGER = LoggerFactory.getLogger(DataSourceContextHolder.class);

    private static final ThreadLocal contextHolder = new ThreadLocal<>(); //實際上就是開啟多個線程,每個線程進行初始化一個數據源
    /**
     * 設置數據源
     * @param dbTypeEnum
     */
    public static void setDbType(DBTypeEnum dbTypeEnum) {
        contextHolder.set(dbTypeEnum.getValue());
    }

    /**
     * 取得當前數據源
     * @return
     */
    public static String getDbType() {
        return (String) contextHolder.get();
    }

    /**
     * 清除上下文數據
     */
    public static void clearDbType() {
        contextHolder.remove();
    }
}

  

6、AOP實現的數據源切換

@Order設置的足夠小是為了讓他先執行

/**
 * aop的實現的數據源切換
* aop切點,實現mapper類找尋,找到所屬大本營以后,如db1Aspect(),則會調用
* db1()前面之前的操作,進行數據源的切換。 */ @Component @Order(value = -100) @Slf4j @Aspect public class DataSourceAspect { @Pointcut("execution(* com.zwyl.bazhong.dao.mapper.db1..*.*(..))") private void db1Aspect() { } @Pointcut("execution(* com.zwyl.bazhong.dao.mapper.db2..*.*(..))") private void db2Aspect() { } @Before("db1Aspect()") public void db1() { log.info("切換到db1 數據源..."); DataSourceContextHolder.setDbType(DBTypeEnum.db1); } @Before("db2Aspect()") public void db2() { log.info("切換到db2 數據源..."); DataSourceContextHolder.setDbType(DBTypeEnum.db2); } }

  

7、mapper層結構

8、寫一個service測試一下

@Service
public class DictServiceImpl implements IDictService {

    @Resource
    private PtDictMapper ptDictMapper; //來自db1

    @Resource
    private SysDictMapper sysDictMapper; // 來自db2


    @Override
    public void getById(String id) {
        PtDict dict = ptDictMapper.selectById("2bf6257fc8fe483c84c1ad7e89d632f6");
        SysDict sysDict = sysDictMapper.getById("49");
        System.out.println("123");
    }
}

 

9、測試結果

 

總結: 其實整個過程可以理解成,配置多數據源 xml中  -------> 然后通過加載多數源到spring工廠中-------->然后創建多線程,每個數據源對應一個數據源--------->然后實際調用時候,會先通過aop匹配到某一具體數據源------------->然后實例化當前數據源

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM