概述

本教程将带你快速了解 HikariCP JDBC 连接池项目。这是一个极其轻量(约130Kb)且性能炸裂的JDBC连接池框架,由 Brett Wooldridge 在2012年左右开发。

简介

网上有不少基准测试对比了HikariCP与其他连接池框架(如*c3p0dbcp2tomcat* 和 vibur)的性能。例如HikariCP团队发布的基准测试结果(原始数据在此):

HikariCP-bench-2.6.0

框架之所以这么快,主要归功于这些技术:

  • 字节码级工程——进行了大量极端的字节码优化(甚至包括汇编级别的本地代码)
  • 微优化——虽然单个优化效果微不足道,但组合起来显著提升整体性能
  • 智能集合框架使用——用自定义的 FastList 替代了 ArrayList<Statement>,消除了范围检查并优化了移除扫描逻辑

Maven依赖

先构建一个示例应用来演示用法。HikariCP支持所有主流JVM版本,每个版本对应不同依赖。Java 8-11使用:

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

也支持旧版JDK(如6/7),具体版本可查看这里这里。最新版本可在Maven中央仓库查询。

使用

现在创建演示应用。注意需在 pom.xml 添加合适的JDBC驱动依赖,否则会抛出 ClassNotFoundException

创建数据源

用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 目录下的属性文件初始化:

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

属性文件格式示例:

dataSourceClassName= //TBD
dataSource.user= //TBD
//其他属性需以dataSource.开头

或使用 java.util.Properties 配置:

Properties props = new Properties();
props.setProperty( "dataSourceClassName" , //TBD );
props.setProperty( "dataSource.user" , //TBD );
//设置其他必需属性
private static HikariConfig config = new HikariConfig( props );

甚至直接初始化数据源:

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

使用数据源

定义好数据源后,就能从连接池获取连接并执行JDBC操作。

假设有两个表 deptemp 模拟员工-部门场景。我们写个类用HikariCP获取数据:

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: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 表有4行数据):

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

总结

本文介绍了HikariCP的优势及配置方法。简单粗暴地说,如果你追求极致性能,它就是最佳选择。

完整源码见GitHub仓库


原始标题:Introduction to HikariCP | Baeldung