在上一節記錄了springboot 整合 Druid 單數據源的全過程,
傳送門:【SpringBoot 2.x】開發實戰day10,整合springboot 與 Druid連接池(一)
源碼鏈接:SpringBoot-Modules-Study/tree/master/springboot-Day10
對於開發人員來說,單一數據源滿足不了項目需要,也就是多數據源很常見,那麼這節記錄多數據源配置和監控。
一、如何配置多數據源
- 引入Maven依賴
- 添加application.yml配置
- 手動創建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