SpringMybatisPlus+ DynamicDataSource自定義封裝

背景:

產品已經上線,目前爲了更好的查看線上數據,所以計劃開發一套boss運營系統,供內部人員使用,boss系統需要建自己的一套數據表數據結構,初期本來想跟業務表放在一起,但是思前想後,Boss系統表結構和業務數據放在一個數據庫不是很合理,所以前期採用動態數據源選擇來加載或者操作合法數據。

實現過程:

1.編寫application.yml --> mybatisplus配置,mysql多數據源配置。

spring:
  application:
    name: @spring.application.name@
    environment: @spring.application.environment@
  datasource.druid:
   #數據源1
    datasource1: 
      url: jdbc:mysql:///mds
      username: root
      password: 123456
      filters: config
      db-type: mysql
      driver-class-name: com.mysql.jdbc.Driver
      initial-size: 1
      min-idle: 1
      max-active: 20
      max-wait: 60000
      min-evictable-idle-time-millis: 300000
      validation-query: SELECT 'x'
      test-while-idle: true
      test-on-borrow: false
      test-on-return: false
      pool-prepared-statements: false
      max-pool-prepared-statement-per-connection-size: 20
      filter:
      stat:
        enabled: false
      wall:
        enabled: true
      web-stat-filter:
        enabled: false
      stat-view-servlet:
        enabled: false
    #數據源1
    datasource1:
      url: jdbc:mysql:///mds
      username: root
      password: 123456
      filters: config
      db-type: mysql
      driver-class-name: com.mysql.jdbc.Driver
      initial-size: 1
      min-idle: 1
      max-active: 20
      max-wait: 60000
      min-evictable-idle-time-millis: 300000
      validation-query: SELECT 'x'
      test-while-idle: true
      test-on-borrow: false
      test-on-return: false
      pool-prepared-statements: false
      max-pool-prepared-statement-per-connection-size: 20
      filter:
      stat:
        enabled: false
      wall:
        enabled: true
      web-stat-filter:
        enabled: false
      stat-view-servlet:
        enabled: false
  session:
    store-type: none
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl
  global-config:
    logic-delete-value: 0
    logic-not-delete-value: 1
  mapper-locations: classpath*:/mapper/*.xml

2.新建DataSourceContextHolder 用於設置,獲取,清空 當前線程內的數據源變量。

/**
 * @program: boss
 * @description:
 * @author: KingdomCoder
 * @create: 2019/1/11 下午10:29
 **/
public class DataSourceContextHolder {

    private static final ThreadLocal<String> CONTEXTHOLDER = new InheritableThreadLocal<>();

    /**
     * 設置數據源
     */
    public static void setDataSource(String db) {
        CONTEXTHOLDER.set(db);
    }

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

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

}

3.新建MultipleDataSource 實現 AbstractRoutingDataSource 類。重寫determineCurrentLookupKey(),通過 DataSourceContextHolder獲取數據源變量,用於當作lookupKey取出指定的數據源。

/**
 * @program: boss
 * @description:
 * @author: KingdomCoder
 * @create: 2019/1/11 下午10:26
 **/
public class MultipleDataSourceConfig extends AbstractRoutingDataSource {

    @Override
    protected Object determineCurrentLookupKey() {
        return DataSourceContextHolder.getDataSource();
    }
}

4.新建DataSourceEnum 用於存放數據源名稱。

/**
 * @program: boss
 * @description:
 * @author: KingdomCoder
 * @create: 2019/1/11 下午10:30
 **/
public enum DataSourceEnum {

    DATASOURCE1("boss"),

    DATASOURCE2("workstation");

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

    @Getter
    private String value;

}

5.新建並配置DataSourceConfig

/**
 * @program: boss
 * @description:
 * @author: KingdomCoder
 * @create: 2019/1/11 下午10:30
 **/
@Configuration
public class DataSourceConfig {

    @Bean("datasource1")
    @ConfigurationProperties("spring.datasource.druid.datasource1")
    public DataSource datasource1() {
        return DruidDataSourceBuilder.create().build();
    }

    @Bean("datasource2")
    @ConfigurationProperties("spring.datasource.druid.datasource2")
    public DataSource datasource2() {
        return DruidDataSourceBuilder.create().build();
    }

    @Bean("multipleDataSource")
    @Primary
    public DataSource multipleDataSource(@Qualifier("datasource1") DataSource datasource1,
        @Qualifier("datasource2") DataSource datasource2) {
        MultipleDataSourceConfig multipleDataSource = new MultipleDataSourceConfig();
        Map<Object, Object> targetDataSources = Maps.newHashMap();
        targetDataSources.put(DataSourceEnum. DATASOURCE1.getValue(), datasource1);
        targetDataSources.put(DataSourceEnum. DATASOURCE2.getValue(), datasource2);
        //添加數據源
        multipleDataSource.setTargetDataSources(targetDataSources);
        //設置默認數據源
        multipleDataSource.setDefaultTargetDataSource(bossDataSource);
        return multipleDataSource;
    }

}

6.新建並配置MybatisConfig:

/**
 * @program: boss
 * @description:
 * @author: KingdomCoder
 * @create: 2019/1/11 下午10:30
 **/
@Configuration
@MapperScan(basePackages = "com.xxx.xxx.xx.mapper")
public class MybatisConfig {

    @Autowired
    private DataSource multipleDataSource;

    @Bean
    public PaginationInterceptor paginationInterceptor() {
        return new PaginationInterceptor();
    }

    @Bean("sqlSessionFactory")
    public SqlSessionFactory sqlSessionFactory() throws Exception {
        MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean();
        sqlSessionFactory.setDataSource(multipleDataSource);
        sqlSessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:/mapper/*/*Mapper.xml"));
        MybatisConfiguration configuration = new MybatisConfiguration();
        configuration.setJdbcTypeForNull(JdbcType.NULL);
        configuration.setCacheEnabled(false);
        sqlSessionFactory.setConfiguration(configuration);
        sqlSessionFactory.setPlugins(new Interceptor[]{
            paginationInterceptor() //添加分頁功能
        });
        return sqlSessionFactory.getObject();
    }

}

7.編寫選擇數據源註解:

/**
 * @program: boss
 * @description:
 * @author: KingdomCoder
 * @create: 2019/1/11 下午10:30
 **/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {

    DataSourceEnum value() default DataSourceEnum. DATASOURCE1;
}

8.編寫切面Aop類DataSourceAspect:

/**
 * @program: boss
 * @description:
 * @author: KingdomCoder
 * @create: 2019/1/11 下午10:30
 **/
@Component
@Slf4j
@Aspect
@Order(-1)
public class DataSourceAspect {

    @Pointcut("@within(com.xxx.xxxx.xxx.dbselection.DataSource) || @annotation(com.xxxx.xxxx.xxxx.DataSource)")
    public void pointCut() {

    }

    @Before("pointCut() && @annotation(dataSource)")
    public void doBefore(DataSource dataSource) {
        log.info("選擇數據源---" + dataSource.value().getValue());
        DataSourceContextHolder.setDataSource(dataSource.value().getValue());
    }

    @After("pointCut()")
    public void doAfter() {
        DataSourceContextHolder.clear();
    }

}

恭喜你只要在方法或者類上加上註解@DataSource(DataSourceEnum. DATASOURCE1)或者@DataSource(DataSourceEnum. DATASOURCE2)可以愉快的選擇自己想要操作的數據源了。

微信公衆號

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章