【SpringBoot 2.x】開發實戰day11,整合springboot 與 Druid連接池(二),多數據源

在上一節記錄了springboot 整合 Druid 單數據源的全過程,
傳送門:【SpringBoot 2.x】開發實戰day10,整合springboot 與 Druid連接池(一)
源碼鏈接:SpringBoot-Modules-Study/tree/master/springboot-Day10


  對於開發人員來說,單一數據源滿足不了項目需要,也就是多數據源很常見,那麼這節記錄多數據源配置和監控。

一、如何配置多數據源

  1. 引入Maven依賴
  2. 添加application.yml配置
  3. 手動創建config,手動加載DataSource、事務管理器,以及SQL工廠(這是與單數據源不同的地方,一定要手動配置,否則不生效

環境: JDK 8 、springBoot 2.1.8、Mybatis 2.1.0、Mysql、Druid

1、引入Maven依賴
<!-- Druid-->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.1.10</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- mysql-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>
<!-- mybatis -->
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.1.0</version>
</dependency>
2、添加application.yml配置
server:
  port: 8888
debug: false
spring:
  # json 輸出格式化與時間格式化
  jackson:
    date-format: yyyy-MM-dd HH:mm:ss SSS
    time-zone: GMT+8
    serialization:
      indent-output: true
  mvc:
    date-format: yyyy-MM-dd
  # datasource
  datasource:
      type: com.alibaba.druid.pool.DruidDataSource
      druid:
        driver-class-name: com.mysql.cj.jdbc.Driver
        # 主庫數據源
        master:
          url: jdbc:mysql://localhost:3306/scott?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8
          username: root
          password: 123456

        # 從庫數據源
        slave:
          url: jdbc:mysql://localhost:3306/shiro?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8
          username: root
          password: 123456

        # Druid連接池配置
        # 數據庫類型爲mysql
        db-type: mysql
        # 啓動時初始化5個連接
        initialSize: 5
        # 最小空閒連接5個
        min-idle: 5
        # 最大連接數量20
        max-active: 20
        # 獲取連接等待時間60秒,超出報錯
        max-wait: 60000
        # 每60秒執行一次連接回收器
        time-between-eviction-runs-millis: 60000
        # 5分鐘內沒有任何操作的空閒連接會被回收
        min-evictable-idle-time-millis: 300000
        # 驗證連接有效性的SQL
        validation-query: select 'x'
        # 空閒時校驗,建議開啓
        test-while-idle: true
        # 使用中是否校驗有效性,推薦關閉
        test-on-borrow: false
        # 歸還連接時校驗有效性,推薦關閉
        test-on-return: false
        # oracle 推薦使用
        pool-prepared-statements: false
        # 設置過濾器,stat用於接收狀態,wall用於防止SQL注入
        filters: stat,wall
        # 支持合併多個DruidDataSource的監控數據
        use-global-data-source-stat: true
        # Druid監控配置
        # WebStatFilter配置
        web-stat-filter:
          # 是否啓用StatFilter, 默認值true
          enabled: true
          # URL白名單
          url-pattern: /*
          # 過濾器排除掉的靜態資源,yml配置需要用引號""
          exclusions:  "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*"
          # session 統計功能
          session-stat-enable: false
          # 最大session數
          session-stat-max-count: 1000
          # 你可以配置principalSessionName, 使得druid能夠知道當前的session的用戶是誰
          principal-session-name: administrator
          # druid能夠知道當前的cookie的用戶是誰
          principal-cookie-name: administrator
          # 配置profileEnable能夠監控單個url調用的sql列表
          profile-enable: true

          # StatViewServlet配置 :展示Druid的統計信息, StatViewServlet的用途包括: 1.提供監控信息展示的html頁面; 2.提供監控信息的JSON API
          stat-view-servlet:

          # 是否啓用StatViewServlet, 默認值true
          enabled: true
          # 根據配置中的url-pattern來訪問內置監控頁面,內置監控頁面的地址是{上下文}/druid
          url-pattern: /druid/*
          # 允許清空統計數據
          reset-enable: false
          # 配置登錄用戶名
		  login-username: administrator
          # 配置登錄密碼
		  login-password: 123456
          # 如果你需要做訪問控制, 可以配置allow和deny這兩個參數
          # deny優先於allow, 如果在deny列表中, 就算在allow列表中, 也會被拒絕. 如果allow沒有配置或者爲空, 則允許所有訪問.
          allow: 127.0.0.1
          deny: 10.1.1.110

        filter:
          stat:
            # 是否啓用statFilter
            enabled: true
            # 數據庫類型
            db-type: mysql
            # 是否開啓慢sql日誌,針對執行效率低的SQL記錄日誌
            log-slow-sql: true
            # 設置超過指定時間爲慢SQL
            slow-sql-millis: 2000

          # WallFilter配置
          wall:
            # 是否啓用WallFilter, 默認值true
            enabled: true
            # 數據庫類型
            db-type: mysql
            config:
              delete-allow: false
              drop-table-allow: false
              alter-table-allow: false
              truncate-allow: false
              # 是否允許非以上基本語句的其他語句, 缺省關閉, 通過這個選項就能夠屏蔽DDL
              none-base-statement-allow: false
              # 檢查UPDATE語句是否無where條件, 這是有風險的, 但不是SQL注入類型的風險
              update-where-none-check: true
              # SELECT ... INTO OUTFILE 是否允許, 缺省是禁止的
              select-into-outfile-allow: false
              # 是否允許調用Connection.getMetadata方法, 這個方法調用會暴露數據庫的表信息
              metadata-allow: true
            # 對被認爲是攻擊的SQL進行LOG.error輸出
            log-violation: true
            # 對被認爲是攻擊的SQL拋出SQLExcepton
            throw-exception: true


# mybatis
mybatis:
  # 核心配置文件,指定的是mybatis-config.xml
  config-location: classpath:/mybatis/mybatis-config.xml
  # 指定mapper映射文件,全註解方式不需要
  mapper-locations: classpath:/mybatis/mapper/*Mapper.xml

# 打印sql
logging:
  level:
     cn.gcheng.springboot.mapper : debug

這裏需要注意兩個配置:
① 配置兩個數據庫,主從庫的配置信息

driver-class-name: com.mysql.cj.jdbc.Driver
# 主庫數據源
master:
  url: jdbc:mysql://localhost:3306/scott?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8
  username: root
  password: 123456

# 從庫數據源
slave:
  url: jdbc:mysql://localhost:3306/shiro?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8
  username: root
  password: 123456

② 支持多個數據源的監控數據

# 支持合併多個DruidDataSource的監控數據
use-global-data-source-stat: true

3、創建配置類,手動創建Bean

① 創建獲取application.yml配置文件的常量基類
DataSourceConfig.java

public class DataSourceConfig {

    /**
     * 表示mapper.xml 映射地址,這一在配置文件中配置不同的位置
     */
    @Value("${mybatis.mapper-locations}")
    protected final String MAPPER_LOCAL = null;

    @Value("${spring.datasource.druid.db-type}")
    protected String dbType;

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

    @Value("${spring.datasource.druid.min-idle}")
    protected int minIdle;

    @Value("${spring.datasource.druid.max-active}")
    protected int maxActive;

    @Value("${spring.datasource.druid.max-wait}")
    protected int maxWait;

    @Value("${spring.datasource.druid.time-between-eviction-runs-millis}")
    protected int timeBetweenEvictionRunsMillis;

    @Value("${spring.datasource.druid.min-evictable-idle-time-millis}")
    protected int minEvictableIdleTimeMillis;

    @Value("${spring.datasource.druid.validation-query}")
    protected String validationQuery;

    @Value("${spring.datasource.druid.test-while-idle}")
    protected boolean testWhileIdle;

    @Value("${spring.datasource.druid.test-on-borrow}")
    protected boolean testOnBorrow;

    @Value("${spring.datasource.druid.test-on-return}")
    protected boolean testOnReturn;

    @Value("${spring.datasource.druid.pool-prepared-statements}")
    protected boolean poolPreparedStatements;

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

    @Value("${spring.datasource.druid.use-global-data-source-stat}")
    protected boolean useGlobalDataSourceStat;

    @Value("${spring.datasource.druid.driver-class-name}")
    protected String driverClassName;

    protected DruidDataSource getDataSourceProperties() {
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName(driverClassName);
        ds.setDbType(dbType);
        ds.setInitialSize(initialSize);
        ds.setMinIdle(minIdle);
        ds.setMaxActive(maxActive);
        ds.setMaxWait(maxWait);
        ds.setTimeBetweenConnectErrorMillis(timeBetweenEvictionRunsMillis);
        ds.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
        ds.setValidationQuery(validationQuery);
        ds.setTestWhileIdle(testWhileIdle);
        ds.setTestOnBorrow(testOnBorrow);
        ds.setTestOnReturn(testOnReturn);
        ds.setPoolPreparedStatements(poolPreparedStatements);
        ds.setUseGlobalDataSourceStat(useGlobalDataSourceStat);

        return ds;
    }
}

② master 主庫數據源配置

@Configuration
// 指定掃描的mapper
@MapperScan(basePackages = {"cn.gcheng.springboot.mapper.master"}, sqlSessionFactoryRef = "masterSqlSessionFactory")
public class MasterDataSourcesConfig extends DataSourceConfig{

    /**
     * 獲取配置文件中數據庫配置屬性, 屬性常量配置在父類中
     */
    @Value("${spring.datasource.druid.master.url}")
    private String url;

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

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


    /**
     * 註冊 master 數據源, @Primary標誌這個 Bean 如果在多個同類 Bean 候選時,該 Bean 優先被考慮。
     * @return
     */
    @Primary
    @Bean("masterDataSource")
    public DataSource masterDataSourceBean() {
        DruidDataSource ds = getDataSourceProperties();
        ds.setUrl(url);
        ds.setUsername(username);
        ds.setPassword(password);
        try {
            ds.setFilters(filters);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return ds;
    }

    /**
     * 註冊 master 事務管理器
     * 用來開啓開啓主庫的事務@Transactional(rollbackFor = Exception.class,value = "masterTransactionManager") value 可以省略
     * @return
     */
    @Primary
    @Bean(name = "masterTransactionManager")
    public DataSourceTransactionManager masterTransactionManager() {
        return new DataSourceTransactionManager(masterDataSourceBean());
    }

    @Primary
    @Bean(name = "masterSqlSessionFactory")
    public SqlSessionFactory masterSqlSessionFactory(@Qualifier("masterDataSource") DataSource dataSource) throws Exception {
        final SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
        sessionFactoryBean.setDataSource(dataSource);
        sessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(MAPPER_LOCAL));
        return sessionFactoryBean.getObject();
    }

}

說明注意事項:
   i:@Primary: 多數據源配置的時候注意,必須要有一個主數據源, 用 @Primary 標誌該 Bean。標誌這個 Bean 如果在多個同類 Bean 候選時,該 Bean優先被考慮。
   ii: dataSource.setFilters(filters): 這個是用來配置 druid 監控sql語句的, 如果你有兩個數據源這個配置哪個數據源就監控哪個數據源的sql,同時配置那就都監控。
  iii: 能夠做到多個數據源的關鍵 就是每個數據源所掃描的mapper包不一樣,誰掃描到哪個mapper那麼該mapper就用哪個數據源,同時都掃到了呢,那當然就得用主數據源咯,也就是添加@Primary 的數據源。

③ 配置從庫數據源:

@Configuration
// 指定掃描的mapper路徑
@MapperScan(basePackages = {"cn.gcheng.springboot.mapper.slave"}, sqlSessionFactoryRef = "slaveSqlSessionFactory")
public class SlaveDataSourcesConfig extends DataSourceConfig{

    /**
     * 獲取配置文件中數據庫配置屬性, 屬性常量配置在父類中
     */
    @Value("${spring.datasource.druid.slave.url}")
    private String url;

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

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

    /**
     * 註冊 slave 數據源
     * @return
     */
    @Bean("slaveDataSource")
    public DataSource slaveDataSourceBean() {
        DruidDataSource ds = getDataSourceProperties();
        ds.setUrl(url);
        ds.setUsername(username);
        ds.setPassword(password);
        try {
            ds.setFilters(filters);
        } catch (SQLException e) {
            e.printStackTrace();

        }

        return ds;
    }

    /**
     * 註冊 slave 事務管理器
     * 用來開啓開啓主庫的事務@Transactional(rollbackFor = Exception.class,value = "slaveTransactionManager"),不同的是value 不可以省略
     * @return
     */
    @Bean(name = "slaveTransactionManager")
    public DataSourceTransactionManager slaveTransactionManager() {
        return new DataSourceTransactionManager(slaveDataSourceBean());
    }

    @Bean(name = "slaveSqlSessionFactory")
    public SqlSessionFactory slaveSqlSessionFactory(@Qualifier("slaveDataSource") DataSource dataSource) throws Exception {
        final SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
        sessionFactoryBean.setDataSource(dataSource);
        sessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(MAPPER_LOCAL));
        return sessionFactoryBean.getObject();
    }
}

   i: 發現從數據源所掃描的mapper和主是完全不一樣的,說明每個數據源負責自己的mapper
   ii: 從數據源是沒有加@Primary
   iii: 這裏也添加了dataSource.setFilters(filters),說明從數據源也需要監聽sql語句。


二、簡單測試和查看監控情況

   啓動項目,訪問配置的 http://localhost:8888/druid 。可以打開,監控有數據,URL訪問也可以檢測到,說明沒問題。
SQL監控:
在這裏插入圖片描述
數據源監控:
   這裏要說明一點,你啓動項目後查看數據源監控可能是這樣的,這不是配置錯誤。
在這裏插入圖片描述
需要訪問後臺,它才加載數據源監控。
主數據源
在這裏插入圖片描述
從數據源
在這裏插入圖片描述
URI監控:
在這裏插入圖片描述


三、參考文檔

https://github.com/alibaba/druid/tree/master/druid-spring-boot-starter
https://www.cnblogs.com/kingsonfu/p/10427408.html
https://www.cnblogs.com/qdhxhz/p/10192041.html


源碼地址

SpringBoot-Modules-Study/tree/master/springboot-Day11

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