《Spring Boot编程实战与面试指南》
03-02:Hikari数据源
使用Java语言编写程序访问数据库之前,除了需要导入对应数据库的驱动程序外,还必须通过驱动管理器创建数据库连接。在SpringBoot 2.x
项目中,默认使用Hikari
连接池管理数据源。当项目pom.xml
引入spring-boot-starter-jdbc
启动器依赖后,即可自动导入Hikari
,该启动器不但依赖它,还会对其自动配置并创建数据源。我们以MySQL数据库为例,介绍如何使用Hikari
。
1、创建项目
- 在Idea中通过
Spring Initializr
方式,创建名称为springboot0302-hikari
的SpringBoot项目并加入如图所示依赖。本案例中Spring Boot的版本为2.4.11。
2. 项目创建成功后,其pom.xml
配置如下。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.11</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.company</groupId>
<artifactId>springboot0302-hikari</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot0302-hikari</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<!-- <dependency>-->
<!-- <groupId>org.springframework.boot</groupId>-->
<!-- <artifactId>spring-boot-starter-data-jpa</artifactId>-->
<!-- </dependency>-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
- 运行主启动程序
Application
,控制台日志中有一条警告信息,而且导致应用程序启动失败。
...
2021-10-12 01:24:43.277 WARN 19552 --- [ restartedMain] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dataSource' defined in class path resource [org/springframework/boot/autoconfigure/jdbc/DataSourceConfiguration$Hikari.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.zaxxer.hikari.HikariDataSource]: Factory method 'dataSource' threw exception; nested exception is org.springframework.boot.autoconfigure.jdbc.DataSourceProperties$DataSourceBeanCreationException: Failed to determine a suitable driver class
2021-10-12 01:24:43.282 INFO 19552 --- [ restartedMain] o.apache.catalina.core.StandardService : Stopping service [Tomcat]
2021-10-12 01:24:43.295 INFO 19552 --- [ restartedMain] ConditionEvaluationReportLoggingListener :
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2021-10-12 01:24:43.313 ERROR 19552 --- [ restartedMain] o.s.b.d.LoggingFailureAnalysisReporter :
***************************
APPLICATION FAILED TO START
***************************
Description:
Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured.
Reason: Failed to determine a suitable driver class
Action:
Consider the following:
If you want an embedded database (H2, HSQL or Derby), please put it on the classpath.
If you have database settings to be loaded from a particular profile you may need to activate it (no profiles are currently active).
Process finished with exit code 0
通过分析上述日志得知,应用程序在启动时,配置名称叫dataSource
的数据源Bean失败,是因为其url
属性未指定导致不能确定合适的驱动程序类,而且也无法配置嵌入式数据源。
注意:默认情况下,驱动程序类是根据url
确定的,故可以不配置。具体原理和过程参考如下源码。
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
org.springframework.boot.autoconfigure.jdbc.DataSourceProperties
org.springframework.boot.jdbc.DatabaseDriver
2、全局配置
1.在全局配置文件application.properties
中,增加spring.datasource.url
配置。
spring.datasource.url=jdbc:mysql://localhost:3306/springbootdata?serverTimezone=UTC&useSSL=false
2.重新运行主启动程序Application
,应用程序即可启动成功。
...
2021-10-12 01:53:00.660 INFO 1992 --- [ restartedMain] com.company.project.Application : Started Application in 2.141 seconds (JVM running for 3.246)
注意:url
的jdbc
和mysql
这两部分书写正确即可启动应用程序,说明根据url
确定驱动程序类型并将其加载成功,但此时并没有创建数据源,否则会报如下异常。
java.lang.IllegalArgumentException: URL must start with 'jdbc'
3、单元测试
1.在主启动程序单元测试类ApplicationTests
中加入自动装配、显示dataSource
信息的代码。
package com.company.project;
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.SQLException;
@SpringBootTest
public class ApplicationTests {
@Autowired
private DataSource dataSource;
@Test
void contextLoads() {
}
@Test
void showDataSource() {
System.out.println("DataSource:" + dataSource);
System.out.println("DataSourceClassName:" + dataSource.getClass().getName());
try {
System.out.println("Connection:" + dataSource.getConnection().toString());
System.out.println("ConnectionIsClosed:" + dataSource.getConnection().isClosed());
System.out.println("ConnectionIsReadOnly:" + dataSource.getConnection().isReadOnly());
System.out.println("ConnectionAutoCommit:" + dataSource.getConnection().getAutoCommit());
System.out.println("ConnectionTransactionIsolation:" + dataSource.getConnection().getTransactionIsolation());
System.out.println("ConnectionNetworkTimeout:" + dataSource.getConnection().getNetworkTimeout());
System.out.println("ConnectionClassName:" + dataSource.getConnection().getClass().getName());
System.out.println("ConnectionCatalog:" + dataSource.getConnection().getCatalog());
System.out.println("DataSource:" + dataSource);
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
2.运行showDataSource()
单元测试方法,尽管已经执行成功,但是,根据控制台日志可知,容器只有在获取dataSource
时才去创建它的Bean,其类名为com.zaxxer.hikari.HikariDataSource
。而且,由于本次登录默认使用了当前Windows用户名和空密码导致dataSource
的连接池创建失败,故其名称为null
。
DataSource:HikariDataSource (null)
DataSourceClassName:com.zaxxer.hikari.HikariDataSource
2021-10-12 02:04:53.303 INFO 8616 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2021-10-12 02:04:54.684 ERROR 8616 --- [ main] com.zaxxer.hikari.pool.HikariPool : HikariPool-1 - Exception during pool initialization.
java.sql.SQLException: Access denied for user 'liuzh'@'localhost' (using password: NO)
...
3.在全局配置文件application.properties
中,增加spring.datasource.username
和spring.datasource.password
配置。
spring.datasource.username=root
spring.datasource.password=123456
4.再次运行showDataSource()
单元测试方法,执行成功,dataSource
的连接池名称默认为HikariPool-1
。
DataSource:HikariDataSource (null)
DataSourceClassName:com.zaxxer.hikari.HikariDataSource
2021-10-12 02:20:59.480 INFO 14180 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2021-10-12 02:20:59.821 INFO 14180 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
Connection:HikariProxyConnection@80052821 wrapping com.mysql.cj.jdbc.ConnectionImpl@eac3a26
ConnectionIsClosed:false
ConnectionIsReadOnly:false
ConnectionAutoCommit:true
ConnectionTransactionIsolation:4
ConnectionNetworkTimeout:0
ConnectionClassName:com.zaxxer.hikari.pool.HikariProxyConnection
ConnectionCatalog:springbootdata
DataSource:HikariDataSource (HikariPool-1)
4、spring-boot-starter-data-jpa
由于启动器spring-boot-starter-data-jpa
依赖spring-boot-starter-jdbc
,省事期间,我们可以在pom.xml
中直接引入它。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
spring-boot-starter-data-jpa.pom文件参考:
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>2.4.11</version>
<name>spring-boot-starter-data-jpa</name>
<description>Starter for using Spring Data JPA with Hibernate</description>
<url>https://spring.io/projects/spring-boot</url>
...
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<version>2.4.11</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
<version>2.4.11</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>jakarta.transaction</groupId>
<artifactId>jakarta.transaction-api</artifactId>
<version>1.3.3</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>jakarta.persistence</groupId>
<artifactId>jakarta.persistence-api</artifactId>
<version>2.2.3</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.4.32.Final</version>
<scope>compile</scope>
<exclusions>
<exclusion>
<artifactId>jaxb-api</artifactId>
<groupId>javax.xml.bind</groupId>
</exclusion>
<exclusion>
<artifactId>javax.activation-api</artifactId>
<groupId>javax.activation</groupId>
</exclusion>
<exclusion>
<artifactId>javax.persistence-api</artifactId>
<groupId>javax.persistence</groupId>
</exclusion>
<exclusion>
<artifactId>jboss-transaction-api_1.2_spec</artifactId>
<groupId>org.jboss.spec.javax.transaction</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>2.4.13</version>
<scope>compile</scope>
<exclusions>
<exclusion>
<artifactId>aspectjrt</artifactId>
<groupId>org.aspectj</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.10</version>
<scope>compile</scope>
</dependency>
</dependencies>
5、Hikari完整配置
name | 描述 | 构造器默认值 | 默认配置validate之后的值 | validate重置 |
autoCommit | 自动提交从池中返回的连接 | TRUE | TRUE | - |
connectionTimeout | 等待来自池的连接的最大毫秒数 | SECONDS.toMillis(30) = 30000 | 30000 | 如果小于250毫秒,则被重置回30秒 |
idleTimeout | 连接允许在池中闲置的最长时间 | MINUTES.toMillis(10) = 600000 | 600000 | 如果idleTimeout+1秒>maxLifetime 且 maxLifetime>0,则会被重置为0(代表永远不会退出);如果idleTimeout!=0且小于10秒,则会被重置为10秒 |
maxLifetime | 池中连接最长生命周期 | MINUTES.toMillis(30) = 1800000 | 1800000 | 如果不等于0且小于30秒则会被重置回30分钟 |
connectionTestQuery | 如果您的驱动程序支持JDBC4,我们强烈建议您不要设置此属性 | null | null | - |
minimumIdle | 池中维护的最小空闲连接数 | -1 | 10 | minIdle<0或者minIdle>maxPoolSize,则被重置为maxPoolSize |
maximumPoolSize | 池中最大连接数,包括闲置和使用中的连接 | -1 | 10 | 如果maxPoolSize小于1,则会被重置。当minIdle<=0被重置为DEFAULT_POOL_SIZE则为10;如果minIdle>0则重置为minIdle的值 |
metricRegistry | 该属性允许您指定一个 Codahale / Dropwizard MetricRegistry 的实例,供池使用以记录各种指标 | null | null | - |
healthCheckRegistry | 该属性允许您指定池使用的Codahale / Dropwizard HealthCheckRegistry的实例来报告当前健康信息 | null | null | - |
poolName | 连接池的用户定义名称,主要出现在日志记录和JMX管理控制台中以识别池和池配置 | null | HikariPool-1 | - |
initializationFailTimeout | 如果池无法成功初始化连接,则此属性控制池是否将 fail fast | 1 | 1 | - |
isolateInternalQueries | 是否在其自己的事务中隔离内部池查询,例如连接活动测试 | FALSE | FALSE | - |
allowPoolSuspension | 控制池是否可以通过JMX暂停和恢复 | FALSE | FALSE | - |
readOnly | 从池中获取的连接是否默认处于只读模式 | FALSE | FALSE | - |
registerMbeans | 是否注册JMX管理Bean(MBeans) | FALSE | FALSE | - |
catalog | 为支持 catalog 概念的数据库设置默认 catalog | driver default | null | - |
connectionInitSql | 该属性设置一个SQL语句,在将每个新连接创建后,将其添加到池中之前执行该语句。 | null | null | - |
driverClassName | HikariCP将尝试通过仅基于jdbcUrl的DriverManager解析驱动程序,但对于一些较旧的驱动程序,还必须指定driverClassName | null | null | - |
transactionIsolation | 控制从池返回的连接的默认事务隔离级别 | null | null | - |
validationTimeout | 连接将被测试活动的最大时间量 | SECONDS.toMillis(5) | 5000 | 如果小于250毫秒,则会被重置回5秒 |
5000 | ||||
leakDetectionThreshold | 记录消息之前连接可能离开池的时间量,表示可能的连接泄漏 | 0 | 0 | 如果大于0且不是单元测试,则进一步判断:(leakDetectionThreshold < SECONDS.toMillis(2) or (leakDetectionThreshold > maxLifetime && maxLifetime > 0),会被重置为0 . 即如果要生效则必须>0,而且不能小于2秒,而且当maxLifetime > 0时不能大于maxLifetime |
dataSource | 这个属性允许你直接设置数据源的实例被池包装,而不是让HikariCP通过反射来构造它 | null | null | - |
schema | 该属性为支持模式概念的数据库设置默认模式 | driver default | null | - |
threadFactory | 此属性允许您设置将用于创建池使用的所有线程的java.util.concurrent.ThreadFactory的实例。 | null | null | - |
scheduledExecutor | 此属性允许您设置将用于各种内部计划任务的java.util.concurrent.ScheduledExecutorService实例 | null | null | - |