HikariCP 简介
1. 概述
在这个介绍性教程中,我们将了解HikariCP JDBC 连接池项目。这是一个非常轻量级(大约 130Kb)和闪电般快速的 JDBC 连接池框架,由Brett Wooldridge 于 2012 年左右开发。
2. 简介
有几个基准测试结果可用于比较 HikariCP 与其他连接池框架的性能,例如*c3p0 、dbcp2 、tomcat *和vibur 。例如,HikariCP 团队发布了以下基准(原始结果可在此处 获得):
该框架之所以如此之快,是因为应用了以下技术:
- **字节码级工程——**一些极端的字节码级工程(包括汇编级本机编码)已经完成
- **微优化——**虽然几乎无法衡量,但这些优化结合起来提高了整体性能
- 集合框架的智能使用——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 是用于初始化数据源的配置类。它带有四个众所周知的必须使用的参数:username、password、jdbcUrl和dataSourceClassName。
在jdbcUrl和dataSourceClassName中,我们通常一次使用一个。但是,当将此属性与较旧的驱动程序一起使用时,我们可能需要设置这两个属性。
除了这些属性之外,还有其他几个可用的属性,我们可能找不到其他池化框架提供的属性:
- 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 相关的操作。
假设我们有两个名为dept和emp的表来模拟员工部门的用例。我们将编写一个类来使用 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() );
}