在上一节记录了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