《Spring Boot编程实战与面试指南》

03-02:Hikari数据源

 

使用Java语言编写程序访问数据库之前,除了需要导入对应数据库的驱动程序外,还必须通过驱动管理器创建数据库连接。在SpringBoot 2.x项目中,默认使用Hikari连接池管理数据源。当项目pom.xml引入spring-boot-starter-jdbc启动器依赖后,即可自动导入Hikari,该启动器不但依赖它,还会对其自动配置并创建数据源。我们以MySQL数据库为例,介绍如何使用Hikari


1、创建项目

  1. 在Idea中通过Spring Initializr方式,创建名称为springboot0302-hikari的SpringBoot项目并加入如图所示依赖。本案例中Spring Boot的版本为2.4.11。

springboot连接hdfs 并配置多个负载 springboot配置hikari_AT阿宝哥


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>
  1. 运行主启动程序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)

注意urljdbcmysql这两部分书写正确即可启动应用程序,说明根据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.usernamespring.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

-