Contents

HikariCP 简介

1. 概述

在这个介绍性教程中,我们将了解HikariCP JDBC 连接池项目。这是一个非常轻量级(大约 130Kb)和闪电般快速的 JDBC 连接池框架,由Brett Wooldridge 于 2012 年左右开发。

2. 简介

有几个基准测试结果可用于比较 HikariCP 与其他连接池框架的性能,例如*c3p0 dbcp2 tomcat *和vibur 。例如,HikariCP 团队发布了以下基准(原始结果可在此处 获得):

https://raw.githubusercontent.com/wiki/brettwooldridge/HikariCP/HikariCP-bench-2.6.0.png

该框架之所以如此之快,是因为应用了以下技术:

  • **字节码级工程——**一些极端的字节码级工程(包括汇编级本机编码)已经完成
  • **微优化——**虽然几乎无法衡量,但这些优化结合起来提高了整体性能
  • 集合框架的智能使用——ArrayList<Statement>被一个自定义类FastList取代,它消除了范围检查并从头到尾执行删除扫描

3. Maven依赖

首先,让我们构建一个示例应用程序来突出它的用法。HikariCP 支持所有主要版本的 JVM。每个版本都需要它的依赖项。对于 Java 8 到 11,我们有:

<dependency>
    <groupId>com.zaxxer</groupId>
    <artifactId>HikariCP</artifactId>
    <version>3.4.5</version>
</dependency>

HikariCP 还支持较旧的 JDK 版本,如 6 和 7。可以分别在此处此处 找到适当的版本。我们还可以在Central Maven Repository 中查看最新版本。

4. 用法

现在我们可以创建一个演示应用程序。请注意,我们需要在pom.xml中包含合适的 JDBC 驱动程序类依赖项。如果没有提供依赖项,应用程序将抛出ClassNotFoundException

4.1. 创建DataSource

我们将使用 HikariCP 的DataSource为我们的应用程序创建一个数据源的单个实例:

public class DataSource {
    private static HikariConfig config = new HikariConfig();
    private static HikariDataSource ds;
    static {
        config.setJdbcUrl( "jdbc_url" );
        config.setUsername( "database_username" );
        config.setPassword( "database_password" );
        config.addDataSourceProperty( "cachePrepStmts" , "true" );
        config.addDataSourceProperty( "prepStmtCacheSize" , "250" );
        config.addDataSourceProperty( "prepStmtCacheSqlLimit" , "2048" );
        ds = new HikariDataSource( config );
    }
    private DataSource() {}
    public static Connection getConnection() throws SQLException {
        return ds.getConnection();
    }
}

这里要注意的一点是static块中的初始化。 HikariConfig 是用于初始化数据源的配置类。它带有四个众所周知的必须使用的参数:usernamepasswordjdbcUrldataSourceClassName

jdbcUrldataSourceClassName中,我们通常一次使用一个。但是,当将此属性与较旧的驱动程序一起使用时,我们可能需要设置这两个属性。

除了这些属性之外,还有其他几个可用的属性,我们可能找不到其他池化框架提供的属性:

  • autoCommit
  • connectionTimeout
  • idleTimeout
  • maxLifetime
  • connectionTestQuery
  • connectionInitSql
  • validationTimeout
  • maximumPoolSize
  • poolName
  • allowPoolSuspension
  • readOnly
  • transactionIsolation
  • leakDetectionThreshold

HikariCP 因其这些数据库属性而脱颖而出。它甚至已经足够先进,可以自行检测连接泄漏。

可以在此处 找到上述属性的详细说明。

我们还可以使用resources 目录中的属性文件来初始化HikariConfig

private static HikariConfig config = new HikariConfig(
    "datasource.properties" );

属性文件应如下所示:

dataSourceClassName= //TBD
dataSource.user= //TBD
//other properties name should start with dataSource as shown above

此外,我们可以使用基于java.util.Properties的配置:

Properties props = new Properties();
props.setProperty( "dataSourceClassName" , //TBD );
props.setProperty( "dataSource.user" , //TBD );
//setter for other required properties
private static HikariConfig config = new HikariConfig( props );

或者,我们可以直接初始化数据源:

ds.setJdbcUrl( //TBD  );
ds.setUsername( //TBD );
ds.setPassword( //TBD );

4.2. 使用数据源

现在我们已经定义了数据源,我们可以使用它从配置的连接池中获取连接,并执行 JDBC 相关的操作。

假设我们有两个名为deptemp的表来模拟员工部门的用例。我们将编写一个类来使用 HikariCP 从数据库中获取这些详细信息。

下面我们将列出创建示例数据所需的 SQL 语句:

create table dept(
  deptno numeric,
  dname  varchar(14),
  loc    varchar(13),
  constraint pk_dept primary key ( deptno )
);
 
create table emp(
  empno    numeric,
  ename    varchar(10),
  job      varchar(9),
  mgr      numeric,
  hiredate date,
  sal      numeric,
  comm     numeric,
  deptno   numeric,
  constraint pk_emp primary key ( empno ),
  constraint fk_deptno foreign key ( deptno ) references dept ( deptno )
);
insert into dept values( 10, 'ACCOUNTING', 'NEW YORK' );
insert into dept values( 20, 'RESEARCH', 'DALLAS' );
insert into dept values( 30, 'SALES', 'CHICAGO' );
insert into dept values( 40, 'OPERATIONS', 'BOSTON' );
 
insert into emp values(
 7839, 'KING', 'PRESIDENT', null,
 to_date( '17-11-1981' , 'dd-mm-yyyy' ),
 7698, null, 10
);
insert into emp values(
 7698, 'BLAKE', 'MANAGER', 7839,
 to_date( '1-5-1981' , 'dd-mm-yyyy' ),
 7782, null, 20
);
insert into emp values(
 7782, 'CLARK', 'MANAGER', 7839,
 to_date( '9-6-1981' , 'dd-mm-yyyy' ),
 7566, null, 30
);
insert into emp values(
 7566, 'JONES', 'MANAGER', 7839,
 to_date( '2-4-1981' , 'dd-mm-yyyy' ),
 7839, null, 40
);

请注意,如果我们使用 H2 等内存数据库,我们需要在运行实际代码之前自动加载数据库脚本以获取数据。幸运的是,H2 带有一个INIT参数,可以在运行时从类路径加载数据库脚本。JDBC URL 应如下所示:

jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;INIT=runscript from 'classpath:/db.sql'

我们需要创建一个方法来从数据库中获取这些数据:

public static List<Employee> fetchData() throws SQLException {
    String SQL_QUERY = "select * from emp";
    List<Employee> employees = null;
    try (Connection con = DataSource.getConnection();
        PreparedStatement pst = con.prepareStatement( SQL_QUERY );
        ResultSet rs = pst.executeQuery();) {
            employees = new ArrayList<>();
            Employee employee;
            while ( rs.next() ) {
                employee = new Employee();
                employee.setEmpNo( rs.getInt( "empno" ) );
                employee.setEname( rs.getString( "ename" ) );
                employee.setJob( rs.getString( "job" ) );
                employee.setMgr( rs.getInt( "mgr" ) );
                employee.setHiredate( rs.getDate( "hiredate" ) );
                employee.setSal( rs.getInt( "sal" ) );
                employee.setComm( rs.getInt( "comm" ) );
                employee.setDeptno( rs.getInt( "deptno" ) );
                employees.add( employee );
            }
	} 
    return employees;
}

然后我们需要创建一个 JUnit 方法来测试它。由于我们知道表emp中的行数,我们可以预期返回列表的大小应该等于行数:

@Test
public void givenConnection_thenFetchDbData() throws SQLException {
    HikariCPDemo.fetchData();
 
    assertEquals( 4, employees.size() );
}