前言:
小伙伴们,大家好,我是狂奔の蜗牛rz,当然你们可以叫我蜗牛君,我是一个学习Java半年多时间的小菜鸟,同时还有一个伟大的梦想,那就是有朝一日,成为一个优秀的Java架构师。
这个SpringBoot基础学习系列用来记录我学习SpringBoot框架基础知识的全过程 (这个系列是参照B站狂神的SpringBoot最新教程来写的,由于是之前整理的,但当时没有发布出来,所以有些地方可能有错误,希望大家能够及时指正!)
之后我将会以一天一更的速度更新这个系列,还没有学习SpringBoot的小伙伴可以参照我的博客学习一下;当然学习过的小伙伴,也可以顺便跟我一起复习一下基础。
最后,希望能够和大家一同进步吧!加油吧!少年们!
废话不多说,让我们开始今天的学习内容吧,由于今天我们来到了SpringBoot基础学习的第七站:整合Druid数据源!
我们来重点介绍Spring Boot 如何集成Druid数据源,如何实现数据库监控,以下是Druid数据源的常用配置参数表:
配置 | 缺省值 | 说明 |
---|---|---|
name (数据源名称) | 配置这个属性的意义在于,如果存在多个数据源,监控时可以通过名字来区分开;如果没有配置,将会生成一个名字,格式为:“DataSource-” + System.identifyHashCode(this) | |
url (url连接) | 连接数据库的url,不同数据库不一样;例如,mysql:jdbc:mysql://localhost:3306/druid2;orcale:jdbc:orcale:thin:@localhost:1521:ocnauto | |
username (用户名) | 连接数据库的用户名 | |
password (密码) | 连接数据库的密码 | |
driverClassName (驱动名) | 根据url自动识别 | 这一项可配可不配,如果不配置Druid会根据url识别数据库类型,然后选择相应的DriverClassName |
initialSize (初始化物理连接个数) | 0 | 初始化时建立物理连接的个数,初始化发生在显式调用init方法,或者第一次getConnection时 |
maxActive (最大连接池数) | 8 | 最大连接池数量 |
maxIdle (最大空闲数) | 8 | 已经不再使用了,配置了也没效果 |
mindle | 最小连接池数量 | |
maxWait (最大等待时间) | 获取连接时最大等待时间,单位毫秒;配置了maxWait之后,缺省启用公平锁,并发效率会有所下降;如果需要可以通过配置useUnicodeLock属性为true,使用非公平锁 | |
poolPreparedStatements (预编译池) | false | 是否缓存preparedStatement,也就是PSCache;PSCache对支持游标的数据库性能提升巨大,比如说Oracle;在MySQL 5.5以下的版本中没有PSCache功能,建议关闭掉;MySQL 5.5以上版本有PSCache,建议开启 |
maxOpenPreparedStatements (开放的最大预编译语句数) | -1 | 要启动PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true;在Druid中,不会存在Orcale下PSCache占内存过多的问题,可以把这个数值配置再大一些,比如说100 |
validationQueryfont> (查询是否生效) | 用来检测连接是否有效的SQL,要求是一个查询语句;如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会起作用;在MySQL中通知Select ‘X’,在Oracle中通常为Select 1 from dual | |
testOnBorrow (检测引入查询语句的连接是否有效) | false | 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能 |
testOnReturn (测试归还连接是否有效) | false | 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能 |
testWhiteIdle (检测空闲连接时间是否大于回收和运行间隔) | false | 建议配置为true,不影响性能,并且保证安全性;申请连接时检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validatioQuery检测连接是否有效 |
timeBetweenEvictionRunsMillis (回收和运行时间间隔) | 有两个含义:Destroy线程检测连接的间隔时间; testWhiteIdle的判断依据 | |
numTestsPerEvictionRun (每个回收和运行的测试数量) | 不再使用,一个DruidDatsSource只支持一个EvictionRun | |
minEvictableTimeMillis (最小回收时间) | Destroy线程,如果检测到当前连接的最后活跃时间和当前时间大于minEvictableTimeMillis,则关闭当前连接 | |
connectionInitSqls (连接初始化SQL) | 物理连接初始化时执行的SQL | |
exceptionSorter (异常分类机) | 根据dbType (数据库类型)自动识别 | 当数据库抛出一些不可恢复的异常时,抛弃连接 |
filters(过滤器) | 属性类型是字符串,通过别名的方式配置扩展插件;常用的插件有:监控统计的filter:stat;日志用的filter:log4j;防御SQL注入的filter:wall | |
proxyFilters (动态过滤器) | 类型是List<com.alibaba.druid.filter.Filter>,如果同时配置filters和ProxyFilters,是组合关系,并非替换关系 |
<!-- Druid数据源资源依赖 --> <!-- https://mvnrepository.com/artifact/com.alibaba/druid --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.12</version> </dependency> <!-- log4j资源依赖 --> <!-- https://mvnrepository.com/artifact/log4j/log4j --> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency>
# 设置服务器端口号 server: port: 8888 # 设置数据库驱动 spring: datasource: username: root password: 123456 # MySQL8.0版本的url链接格式 # url: jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC url: jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf-8&useSSL=true # com.mysql.cj.jdbc.Driver是8.0版本以上的数据库 # driver-class-name: com.mysql.cj.jdbc.Driver # com.mysql.jdbc.Driver是8.0以下版本的数据库 driver-class-name: com.mysql.jdbc.Driver # 使用Druid的数据源 type: com.alibaba.druid.pool.DruidDataSource # Spring Boot 默认是不注入这些属性值的,因此需要自己来配置 # 设置Druid数据源的基本配置 # 初始化时的物理连接个数:初始化发生在显式调用init方法或者第一次getConnection时 initialSize: 5 # 最小连接池数量 minIdle: 5 # 最大连接池数量 maxActive: 20 # 获取连接的最大等待时间,时间单位是毫秒 maxWait: 60000 # 运行和回收的间隔时间:即销毁线程检测连接的间隔时间 timeBetweenEvictionRunsMillis: 300000 # 最小回收时间:如果检测到当前连接的最后活跃时间和当前时间大于最小回收时间,则关闭当前连接 minEvictableIdleTimeMillis: 300000 # 判断连接到的查询语句是否有效 validationQuery: Select 1 from dual # 检测空闲连接时间:建议设置为true,申请连接检测,如果空闲时间大于回收运行间隔时间,执行检测连接到的查询语句是否生效 testWhiteIdle: true # 检测引入的查询语言的连接是否生效:此配置会降低性能,默认值为false testOnBorrow: false # 检测归还连接时执行生效查询语句是否有效:此配置会降低性能 testOnReturn: false # 是否缓存预编译语句池:即使用PSCache,默认值为false,MySQL5.5以上版本有PSCache,建议开启 poolPreparedStatements: true # 配置监控拦截的filters:监控统计-stat;日志记录-log4j;防御SQL注入:wall # 如果运行时报错-java.lang.ClassNotFoundException:org.apache,log4j.Priority # 则导入log4j依赖即可,Maven仓库地址:https://mvnrepository.com/artifact/log4j/log4j filters: stat,log4j,wall # 设置每个连接的最大池预编译语句大小 maxPoolPreparedStatementPerConnectionSize: 20 # 设置使用全局的数据源的监控统计拦截器stat useGlobalDataSourceStat: true # 设置连接属性:监控统计拦截器stat的mergeSql(合并SQL)属性为true,即此配置生效;慢SQL时间为0.5秒 connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
package com.kuang; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import javax.sql.DataSource; import java.sql.Connection; import java.sql.SQLException; @SpringBootTest class Springboot04DataApplicationTests { // 使用@Autowired注解将DataSource自动装配到Spring容器中 @Autowired DataSource dataSource; // 数据源 @Test void contextLoads() throws SQLException { // 查看默认的数据源:class com.zaxxer.hikari.HikariDataSource,相当于DBCP等数据源 System.out.println(dataSource.getClass()); // 获取数据库连接 Connection connection = dataSource.getConnection(); // 打印connection连接信息 System.out.println(connection); // xxx Template:SpringBoot已经配置好模板bean,拿来即用 CRUD // 关闭数据库连接 connection.close(); } }
// 动态注册器Bean public abstract class DynamicRegistrationBean<D extends Dynamic> extends RegistrationBean { // 通过日志工厂获取Log日志信息 private static final Log logger = LogFactory.getLog(RegistrationBean.class); private String name; // 名字 private boolean asyncSupported = true; // 开启异步支持 // 通过创建链表HashMap对象,使用Map键值对形式来存储初始化参数 private Map<String, String> initParameters = new LinkedHashMap(); // 动态注册器Bean的无参构造器 public DynamicRegistrationBean() { } // 设置名字 public void setName(String name) { // 断言名字不能为空 Assert.hasLength(name, "Name must not be empty"); this.name = name; } // 设置异步支持 public void setAsyncSupported(boolean asyncSupported) { this.asyncSupported = asyncSupported; } // 获取异步支持 public boolean isAsyncSupported() { return this.asyncSupported; } // 设置初始化参数方法 public void setInitParameters(Map<String, String> initParameters) { // 断言初始化参数是否为空 Assert.notNull(initParameters, "InitParameters must not be null"); // 如果为空,通过创建链式HashMap对象来初始化参数 this.initParameters = new LinkedHashMap(initParameters); } //...(省略后面部分代码)... }
package com.kuang.config; import com.alibaba.druid.pool.DruidDataSource; import com.alibaba.druid.support.http.StatViewServlet; import com.alibaba.druid.support.http.WebStatFilter; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.boot.web.servlet.ServletRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import javax.sql.DataSource; import java.util.HashMap; import java.util.Map; // 将DruidConfig注册为配置类 @Configuration public class DruidConfig { // 将Druid数据源与spring的datasource配置文件关联 @ConfigurationProperties(prefix = "spring.datasource") // 将Druid数据源当做组件,注册到Spring容器中 @Bean // 获取DruidDataSource的方法 public DataSource druidDataSource() { return new DruidDataSource(); } /** * 后台监控:web.xml和ServletRegistrationBean * 因为SpringBoot内置了Servlet容器,所以没有web.xml,替代方法:使用ServletRegistrationBean */ // 将后台监控statViewServlet,当做组件注册到Spring容器中 @Bean public ServletRegistrationBean statViewServlet() { // 创建Servlet注册Bean:有两个参数:第一个是监控视图过滤器,第二个是配置一个访问页面 ServletRegistrationBean<StatViewServlet> bean = new ServletRegistrationBean<>(new StatViewServlet(),"/druid/*"); // 后台需要有人登录,账号密码配置 // 创建HashMap集合,存放账户密码键值对 HashMap<String, String>initParameters = new HashMap<>(); // 增加配置 // 登录的key是固定的:loginUsername和loginPassword initParameters.put("loginUsername","admin"); initParameters.put("loginPassword","123456"); // 允许谁可以访问 initParameters.put("allow",""); // 禁止谁能访问 // initParameters.put("zhangsan", "192.168.1.2"); // 设置初始化参数 bean.setInitParameters(initParameters); // 返回Servlet注册Bean return bean; } // Web后台监控过滤器 public FilterRegistrationBean webStatFilter() { // 创建过滤器注册Bean FilterRegistrationBean bean = new FilterRegistrationBean(); // 设置过滤器:通过Web后台监控过滤器 bean.setFilter(new WebStatFilter()); // 可以过滤哪些请求 // 创建HashMap集合:存放不过滤信息的键值对 Map<String, String> initParameters = new HashMap<>(); // 设置不进行过滤的信息 initParameters.put("exclusions", "*.js,*.css,/druid/*"); // 设置初始化参数 bean.setInitParameters(initParameters); // 返回过滤器注册Bean return bean; } }
package com.kuang.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.List; import java.util.Map; // 使用@RestController注解,实现Controller接口并且返回值为字符串 @RestController public class JdbcController { // 使用@Autowired注解,自动装配JdbcTemplate(JDBC模板)类到Spring容器中 @Autowired JdbcTemplate jdbcTemplate; /** * 查询用户列表信息 * 没有实体类,数据库中的内容,怎么获取?可以使用万能的Map */ /** * 使用@RequestMapping注解,设置请求映射路径 * 真实访问路径:http://localhost:8080/getList */ @RequestMapping("/getList") public List<Map<String, Object>> getUserList() { // 封装SQL查询语句到字符串sql中去 String sql = "select * from user"; // 调用JDBC模板引擎的queryForList来获取用户数组集合信息 List<Map<String, Object>> list_maps = jdbcTemplate.queryForList(sql); // 返回到用户集合 return list_maps; } }
结果:查询用户列表信息成功!
结果:SQL监控记录了执行查询SQL语句的操作!
package com.kuang.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.List; import java.util.Map; //使用@RestController注解,实现Controller接口并且返回值为字符串 @RestController public class JdbcController { //使用@Autowired注解,自动装配JdbcTemplate(JDBC模板)类到Spring容器中 @Autowired JdbcTemplate jdbcTemplate; //修改用户信息 //真实访问路径:http://localhost:8080/updateUser //使用@RequestMapping注解,设置请求映射路径 @RequestMapping("/deleteUser/{userId}") public String delteUser(@PathVariable("userId") int id) { //封装SQL修改语句到字符串sql中去 String sql = "delete from mybatis.user where id=?"; //JDBC模板调用update方法进行修改数据 jdbcTemplate.update(sql,id); //返回一个"Update-OK" return "Delete-OK"; } }
结果:删除指定用户信息成功!
结果:SQL监控记录了执行删除SQL语句操作!
结论:
通过上面的测试,我们体会到了Druid数据源强大的SQL监控功能,不仅能够监测SQL语句的执行,包括执行次数、执行时间和更新行数、读取行数,以及最大并发操作和执行时间分布
好了,今天的有关 SpringBoot基础学习之整合Druid数据源 的学习就到此结束啦,欢迎小伙伴们积极学习和讨论,喜欢的可以给蜗牛君点个关注,顺便来个一键三连,我们下期见,拜拜啦!
参考视频链接:https://www.bilibili.com/video/BV1PE411i7CV(【狂神说Java】SpringBoot最新教程IDEA版通俗易懂)