在Spring Boot中使用国产数据库连接池Druid

news2025/1/10 20:20:04

在我们实际开发过程中,我们经常使用的是DriverManager来获取,通过每次都向数据库建立连接时将Connection加载到内存中,然后验证用户名和密码,这段时间的消耗大致在0.0 5s - 1s左右,每次当我们需要获取数据库连接的时候,就需要向数据库要求一次,执行之后在断开连接,这样的重复的操作,在一定程度上也会消耗部分时间以及资源,数据库的连接资源并未得到充分的利用,假设,当同时有几百人或者几千人在线,那么频繁的进行数据库连接操作,将会占用很多系统资源,眼中的甚至造成服务器崩溃。

为了避免这种传统开发过程中的数据库连接问题,开发者便尝试采用数据库连接池技术。其基本思想就是: 为数据库连接建立一个“缓冲池“。预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时候,只需要从“缓冲池“中取出一个,然后再使用完毕后放回去。

在此文中,我们将使用由我们阿里巴巴数据库事业部出品的一个开源项目,他除了是一个高性能数据库连接池之外,更是一个自带监控的数据库连接池。我们在《Spring Boot中配置默认的HikariCP数据源》中围绕了Hikari进行了相关讲解,知道他虽然很优秀,但是对于国内的用户来说,我们可能对Druid更加熟悉,故而关于在Spring Boot中使用Druid是我们作为后端开发人员需要掌握的一项基本技能。

配置Druid数据源

仍旧是新建一个Spring Boot项目
第一步,引入相关依赖。

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-jdbc</artifactId>
		</dependency>
		<!--引入Druid依赖-->
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>druid-spring-boot-starter</artifactId>
			<version>1.1.21</version>
		</dependency>
<!--进行监控-->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-actuator</artifactId>
		</dependency>

		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
		</dependency>

		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<scope>provided</scope>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>

		<dependency>
			<groupId>com.oracle.ojdbc</groupId>
			<artifactId>ojdbc8</artifactId>
			<version>19.3.0.0</version>
		</dependency>
	</dependencies>

第二步,在Spring Boot的application.properties中配置数据库连接信息。
Druid的配置都是以spring.datasource.druid作为前缀的

# MySQL基础配置
#spring.datasource.druid.url=jdbc:mysql://localhost:3306/test
#spring.datasource.druid.username=root
#spring.datasource.druid.password=
#spring.datasource.druid.driver-class-name=com.mysql.cj.jdbc.Driver

server.port=8088

# Oracle
spring.datasource.druid.url=jdbc:oracle:thin:@localhost:1521/orcl
spring.datasource.druid.username=root
spring.datasource.druid.password=root
spring.datasource.druid.driver-class-name=oracle.jdbc.driver.OracleDriver
# 连接池配置
spring.datasource.druid.initialSize=10
spring.datasource.druid.maxActive=20
spring.datasource.druid.maxWait=60000
spring.datasource.druid.minIdle=1
spring.datasource.druid.timeBetweenEvictionRunsMillis=60000
spring.datasource.druid.minEvictableIdleTimeMillis=300000
spring.datasource.druid.testWhileIdle=true
spring.datasource.druid.testOnBorrow=true
spring.datasource.druid.testOnReturn=false
spring.datasource.druid.poolPreparedStatements=true
spring.datasource.druid.maxOpenPreparedStatements=20
spring.datasource.druid.validationQuery=SELECT 1 from useradd
spring.datasource.druid.validation-query-timeout=500
spring.datasource.druid.filters=stat,wall

# 监控配置
spring.datasource.druid.stat-view-servlet.enabled=true
spring.datasource.druid.stat-view-servlet.url-pattern=/druid/*
spring.datasource.druid.stat-view-servlet.reset-enable=true
spring.datasource.druid.stat-view-servlet.login-username=admin
spring.datasource.druid.stat-view-servlet.login-password=admin

属性参照报表:

配置缺省值说明
name 名字配置这个属性的意义在于,如果存在多个数据源,监控的时候可以通过名字来区分开来。如果没有配置,将会生成一个名字,格式是:“DataSource-” + System.identityHashCode(this). 另外配置此属性至少在1.0.5版本中是不起作用的,强行设置name会出错。详情-点此处。
url连接数据库的url,不同数据库不一样。例如: mysql : jdbc:mysql://10.20.153.104:3306/druid2 oracle : jdbc:oracle:thin:@10.20.149.85:1521:ocnauto
username 用户名连接数据库的用户名
password连接数据库的密码。如果你不希望密码直接写在配置文件中,可以使用ConfigFilter。详细看这里
driverClassName 驱动程序类名称根据url自动识别这一项可配可不配,如果不配置druid会根据url自动识别dbType,然后选择相应的driverClassName
initialSize0初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时
maxActive8最大连接池数量
maxIdle 最大空闲8已经不再使用,配置了也没效果
minIdle最小连接池数量
maxWait获取连接时最大等待时间,单位毫秒。配置了maxWait之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置useUnfairLock属性为true使用非公平锁。
poolPreparedStatements poolPrepared语句false是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle。在mysql下建议关闭。
maxPoolPreparedStatementPerConnectionSize-1要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true。在Druid中,不会存在Oracle下PSCache占用内存过多的问题,可以把这个数值配置大一些,比如说100
validationQuery用来检测连接是否有效的sql,要求是一个查询语句,常用select ‘x’。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会起作用。
validationQueryTimeout单位:秒,检测连接是否有效的超时时间。底层调用jdbc Statement对象的void setQueryTimeout(int seconds)方法
testOnBorrowtrue申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。
testOnReturn testOnReturn(测试返回)false归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。
testWhileIdlefalse 假建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效
keepAlivefalse(1.0.28)连接池中的minIdle数量以内的连接,空闲时间超过minEvictableIdleTimeMillis,则会执行keepAlive操作。
timeBetweenEvictionRunsMillis1分钟(1.0.14)有两个含义:1) Destroy线程会检测连接的间隔时间,如果连接空闲时间大于等于minEvictableIdleTimeMillis则关闭物理连接。2) testWhileIdle的判断依据,详细看testWhileIdle属性的说明
numTestsPerEvictionRun30分钟(1.0.14)不再使用,一个DruidDataSource只支持一个EvictionRun
minEvictableIdleTimeMillis连接保持空闲而不被驱逐的最小时间
connectionInitSqls物理连接初始化的时候执行的sql
exceptionSorter 例外分类器根据dbType自动识别当数据库抛出一些不可恢复的异常时,抛弃连接
filters属性类型是字符串,通过别名的方式配置扩展插件,常用的插件有:监控统计用的filter:stat日志用的filter:log4j防御sql注入filter:wall
proxyFilters类型是List<com.alibaba.druid.filter.Filter>,如果同时配置了filters和proxyFilters,是组合关系,并非替换关系

配置开启stat监控统计的界面以及监控内容的相关配置:

spring.datasource.druid.stat-view-servlet.url-pattern:访问地址规则
spring.datasource.druid.stat-view-servlet.reset-enable:是否允许清空统计数据
spring.datasource.druid.stat-view-servlet.login-username:监控页面的登录账户
spring.datasource.druid.stat-view-servlet.login-password:监控页面的登录密码

第三步:创建相关实体类User

public class User {

    private String name;
    private Integer age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    public User(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public User() {
    }
}

第四步:创建相关业务接口:

public interface UserService {

    /**
     * 新增一个用户
     *
     * @param name
     * @param age
     */
    int create(String name, Integer age);

    /**
     * 根据name查询用户
     *
     * @param name
     * @return
     */
    List<User> getByName(String name);

    /**
     * 根据name删除用户
     *
     * @param name
     */
    int deleteByName(String name);

    abstract int getAllUsers();

    /**
     * 删除所有用户
     */
    int deleteAllUsers();

}

第五步:实现接口

@Service
public class UserServiceImpl implements UserService {

    private JdbcTemplate jdbcTemplate;

    UserServiceImpl(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    @Override
    public int create(String name, Integer age) {
        return jdbcTemplate.update("insert into USERADD(NAME, AGE) values(?, ?)", name, age);
    }

    @Override
    public List<User> getByName(String name) {
        List<User> users = jdbcTemplate.query("select NAME, AGE from USERADD where NAME = ?", (resultSet, i) -> {
            User user = new User();
            user.setName(resultSet.getString("NAME"));
            user.setAge(resultSet.getInt("AGE"));
            return user;
        }, name);
        return users;
    }

    @Override
    public int deleteByName(String name) {
        return jdbcTemplate.update("delete from USERADD where NAME = ?", name);
    }

    @Override
    public int getAllUsers() {
        return jdbcTemplate.queryForObject("select count(1) from USERADD", Integer.class);
    }

    @Override
    public int deleteAllUsers() {
        return jdbcTemplate.update("delete from USERADD");
    }

}

第六步:创建相关控制层,调用业务层

@RestController
public class UserController {

    @Autowired
    private UserService userService;

    @PostMapping("/user")
    public int create(@RequestBody User user) {
        return userService.create(user.getName(), user.getAge());
    }

    @GetMapping("/user/{name}")
    public List<User> getByName(@PathVariable String name) {
        return userService.getByName(name);
    }

    @DeleteMapping("/user/{name}")
    public int deleteByName(@PathVariable String name) {
        return userService.deleteByName(name);
    }

    @GetMapping("/user/count")
    public int getAllUsers() {
        return userService.getAllUsers();
    }

    @DeleteMapping("/user/all")
    public int deleteAllUsers() {
        return userService.deleteAllUsers();
    }

}

在这里插入图片描述
在这里插入图片描述

http://localhost:8088/druid/login.html
admin – admin
在这里插入图片描述
在这里插入图片描述
数据源:这里可以看到之前我们配置的数据库连接池信息以及当前使用情况的各种指标。
在这里插入图片描述

SQL监控:该数据源中执行的SQL语句极其统计数据。在这个页面上,我们可以很方便的看到当前这个Spring Boot都执行过哪些SQL,这些SQL的执行频率和执行效率也都可以清晰的看到。如果你这里没看到什么数据?别忘了我们之前创建了一个Controller,用这些接口可以触发UserService对数据库的操作。所以,这里我们可以通过调用接口的方式去触发一些操作,这样SQL监控页面就会产生一些数据:

在这里插入图片描述
图中监控项上,执行时间、读取行数、更新行数都通过区间分布的方式表示,将耗时分布成8个区间:

0 - 1 耗时0到1毫秒的次数
1 - 10 耗时1到10毫秒的次数
10 - 100 耗时10到100毫秒的次数
100 - 1,000 耗时100到1000毫秒的次数
1,000 - 10,000 耗时1到10秒的次数
10,000 - 100,000 耗时10到100秒的次数
100,000 - 1,000,000 耗时100到1000秒的次数
1,000,000 - 耗时1000秒以上的次数

SQL防火墙:该页面记录了与SQL监控不同维度的监控数据,更多用于对表访问维度、SQL防御维度的统计。
在这里插入图片描述
该功能数据记录的统计需要在spring.datasource.druid.filters中增加wall属性才会进行记录统计,比如这样:

spring.datasource.druid.filters=stat,wall

注意:这里的所有监控信息是对这个应用实例的数据源而言的,而并不是数据库全局层面的,可以视为应用层的监控,不可能作为中间件层的监控。

编写测试类:

@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest
public class Chapter33ApplicationTests {

    @Autowired
    private UserService userSerivce;

    @Autowired
    private DataSource dataSource;

    @Before
    public void setUp() {
        // 准备,清空user表
        userSerivce.deleteAllUsers();

    }

    @Test
    public void test() throws Exception {
        // 插入5个用户
        userSerivce.create("Tom", 10);
        userSerivce.create("Mike", 11);
        userSerivce.create("Didispace", 30);
        userSerivce.create("Oscar", 21);
        userSerivce.create("Linda", 17);

        // 查询名为Oscar的用户,判断年龄是否匹配
        List<User> userList = userSerivce.getByName("Oscar");
        Assert.assertEquals(21, userList.get(0).getAge().intValue());

        // 查数据库,应该有5个用户
        Assert.assertEquals(5, userSerivce.getAllUsers());

        // 删除两个用户
        userSerivce.deleteByName("Tom");
        userSerivce.deleteByName("Mike");

        // 查数据库,应该有5个用户
        Assert.assertEquals(3, userSerivce.getAllUsers());

    }

}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1170916.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

“利用自动粘贴功能,一键粘贴网址,提升工作效率“

在快节奏的现代工作中&#xff0c;效率是关键。如果你经常需要复制和粘贴网址&#xff0c;那么你可能会浪费很多时间。幸运的是&#xff0c;我们的自动粘贴功能可以帮助你自动粘贴网址&#xff0c;一键即可完成&#xff0c;让你更加高效地工作。 首先&#xff0c; 我们要进入首…

操作系统复习(1)概述

一、序言 1.1简介 计算机系统组成&#xff1a;硬件操作系统 操作系统是计算机系统中的一个重要组成部分&#xff0c;它负责管理和控制计算机的硬件资源和软件资源&#xff0c;以及提供用户接口和其他功能 操作系统定义&#xff1a;操作系统是计算机系统中的一个系统软件&…

代码随想录算法训练营第23期day38|动态规划理论基础、509. 斐波那契数、70. 爬楼梯、746. 使用最小花费爬楼梯

目录 一、动态规划理论基础 1.动态规划的解题步骤 2.动态规划应该如何debug 二、&#xff08;leetcode 509&#xff09;斐波那契数 1.递归解法 2.动态规划 1&#xff09;确定dp数组以及下标的含义 2&#xff09;确定递推公式 3&#xff09;dp数组如何初始化 4&#x…

PMIC、电源管理MAX77646ANP、MAX77647AANP、MAX77675AEWE、MAX77847AEWL DC-DC 开关稳压器

一、MAX77646ANP、MAX77647AANP 低IQ SIMO PMIC支持原电池应用的1.8V工作电压 MAX77646/MAX77647为尺寸和效率至关重要的低功耗应用提供电源解决方案。该IC集成单电感多输出(SIMO)降压/升压稳压器&#xff0c;可通过单个电感提供三个可独立编程的电源轨&#xff0c;尽可能地减…

PageRank算法c++实现

首先用邻接矩阵A表示从页面j到页面i的概率&#xff0c;然后根据公式生成转移概率矩阵 M&#xff08;1-d&#xff09;*Qd*A 常量矩阵Q(qi,j),qi,j1/n 给定点击概率d&#xff0c;等级值初始向量R0&#xff0c;迭代终止条件e&#xff1b; 计算Ri1M*R…

选哪个内衣洗衣机比较好?觉飞和希亦内衣洗衣机对比测评

如今&#xff0c;随着物质生活水平的不断提升&#xff0c;消费者更加追求健康、品质化的生活。而反映在衣物清洁上&#xff0c;诸如像贴身衣物的深度清洁、儿童和成人衣物的分洗等细分场景下的洗护需求也变得越来越多&#xff0c;内衣物洗衣机也因此应运而生。现在市面上关于内…

【RabbitMQ】RabbitMQ 消息的堆积问题 —— 使用惰性队列解决消息的堆积问题

文章目录 一、消息的堆积问题1.1 什么是消息的堆积问题1.2 消息堆积的解决思路 二、惰性队列解决消息堆积问题2.1 惰性队列和普通队列的区别2.2 惰性队列的声明方式2.3 演示惰性队列接收大量消息2.4 惰性队列的优缺点 一、消息的堆积问题 1.1 什么是消息的堆积问题 消息的堆积…

修复dinput8.dll文件的缺失,以及修复dinput8.dll文件时需要注意什么

dinput8.dll文件通常在使用大型游戏时容易出现dinput8.dll文件丢失的情况&#xff0c;今天这篇文章将要教大家修复dinput8.dll文件的缺失&#xff0c;同时在修复dinput8.dll文件时需要注意些什么&#xff1f;防止文件在修复的过程中出现其他的错误。 dinput8.dll是DirectInput库…

[自定义 Vue 组件] 小尾巴下拉菜单组件(2.0) TailDropDown

文章归档&#xff1a;https://www.yuque.com/u27599042/coding_star/kcoem6dgyn8drglb [自定义 Vue 组件] 下拉菜单(1.0) DropDownMenu&#xff1a;https://www.yuque.com/u27599042/coding_star/llltv52tchmatwg4 组件效果示例 组件所依赖的常量 在 src 目录下&#xff0c;创…

商品礼品卡券提货码商城小程序开发

礼品商品提货码卡密卷商城系统开发 本系统为方便各类商品、产品福利等&#xff0c;能以电子化形式发放&#xff0c;用通过手机端直接邮寄提货而开发使用。 主要功能包含&#xff1a;1、后台批量卡密生产2、前端卡密输入兑换提货 应用界面 1、兑换页输入卡券账号和密码&#…

模拟实现简易版shell(需要单独处理 ls+cd+export)

目录 minishell -- 简易版shell 大致思路 注意点 ls cd export 代码 minishell -- 简易版shell 大致思路 首先确定,我们的shell是一直在运行的(while(1))每次都会有提示信息打印出来 [xxxxxx x]输入命令(选项),其中,我们需要将读入的字符串分开,不然只是没有意义的字符…

HNU程序设计 练习五-函数

1.小熊买糖果 【问题描述】 小熊去到商店&#xff0c;选择了一种它非常喜欢的糖果&#xff0c;其单价为 k 元&#xff0c;假定商店里有无穷多的这种糖果。 它的父亲允许它花费任意多的10元硬币和一个 r 元硬币去购买&#xff0c;但不能找零&#xff0c;请帮助小熊确定它能购买…

Docker基础(简单易懂)

目录 一、docker是什么 核心概念 二、docker安装 1、卸载docker 2、使用yum 安装 三、docker常用命令 1、帮助命令 2、镜像命令 1&#xff09;查看镜像 2&#xff09;查询镜像 3&#xff09;拉取镜像 4&#xff09;删除镜像 3、容器命令 四、容器数据卷 五、Dock…

在全球半导体行业中“疯狂冒险”的台积电

来源&#xff1a;猛兽财经 作者&#xff1a;猛兽财经 总结&#xff1a; &#xff08;1&#xff09;尽管在收入和利润率方面面临挑战&#xff0c;但台积电(TSM)仍在为自己在半导体行业的复苏做准备。 &#xff08;2&#xff09;台积电的全球扩张和先进技术战略使其能够充分利用…

Windows10+CLion+OpenCV4.5.2开发环境搭建

目录 OpenCV准备下载解压环境变量配置 clion配置新建工程选择编译工具编写CMakeLists.txt将OpenCV执行的一些动态链接库复制到项目目录下编写测试代码 OpenCV准备 下载 官网&#xff1a;OpenCV 往下滑&#xff0c;找到自己需要下载的版本。我下载的是4.5.2。 解压 选择好解…

dockerfile避坑笔记(VMWare下使用Ubuntu在Ubuntu20.04基础镜像下docker打包多个go项目)

一、docker简介 docker是一种方便跨平台迁移应用的程序&#xff0c;通过docker可以实现在同一类操作系统中&#xff0c;如Ubuntu和RedHat两个linux操作系统中&#xff0c;实现程序的跨平台部署。比如我在Ubuntu中打包了一个go项目的docker镜像&#xff08;镜像为二进制文件&am…

“如何对TXT文件的内容进行连续行删除?实现一键文件整理!

如果你有一个TXT文件&#xff0c;需要删除其中的连续行&#xff0c;这可能是为了整理文件、去除重复信息或清除不需要的文本。尽管手动删除每一行可能很耗时&#xff0c;但幸运的是&#xff0c;有一个简单而高效的方法可以帮助你实现这个目标。 首先&#xff0c;在首助编辑高手…

05-流媒体-RTMP协议介绍

05-流媒体-RTMP协议介绍 1.RTMP概述 RTMP是一种常见的流媒体协议,是基于TCP/IP协议模型的应用层协议,工作在TCP协议上,端口是1935。通过TCP三次握手保证传输的可靠。 2.握手 2.1 握手过程 RTMP基于传输层TCP连接后,通过RTMP握手协议来完成RTMP连接。客户端和服务端各发…

干货分享:基于 LSTM 的广告库存预估算法

近年来&#xff0c;随着互联网的发展&#xff0c;在线广告营销成为一种非常重要的商业模式。出于广告流量商业化售卖和日常业务投放精细化运营的目的&#xff0c;需要对广告流量进行更精准的预估&#xff0c;从而更精细的进行广告库存管理。 因此&#xff0c;携程广告纵横平台…

全新干货!一招教你迅速提升流量主收入!包你轻松月入过万

也不怕大家笑话&#xff0c;才哥以前收入每天才一块钱&#xff0c;连瓶水都买不了&#xff0c; 可是自从我开始接触老年粉私域后&#xff0c;一个搬运公众号的流量主收益两个月后就可以用“浴火重生”来形容了。 一个搬运公众号一天的流量主收益比我原创两年的个人公众号收益还…