Connection Pooling: HikariCP Standalone Configuration
Creating a new database connection for every query is slow: a TCP handshake and TLS negotiation can take 50–200 ms. A connection pool keeps a set of ready connections that threads borrow and return. HikariCP is the fastest and most widely used JDBC pool for Java, and it is what Spring Boot uses by default. This article configures it from scratch, without Spring Boot auto-configuration, so you understand every important property.
Why You Need a Connection Pool
Without a pool, every call to DriverManager.getConnection() creates a brand new connection:
| Step | Typical Cost |
|---|---|
| TCP connect | 1–5 ms (local) / 50–200 ms (remote) |
| TLS handshake | 10–50 ms |
| DB authentication | 5–20 ms |
| Total per query (no pool) | 60–270 ms overhead |
| Total per query (with pool) | <1 ms (borrow from pool) |
A pool also caps the number of concurrent connections to the database, preventing connection exhaustion.
Maven Dependency
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>5.1.0</version>
</dependency>
<!-- Plus your JDBC driver -->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.7.3</version>
</dependency>
Basic HikariCP Setup
Create a HikariConfig, configure it, then build a HikariDataSource. The DataSource is the pool: keep it as a singleton in your application:
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
public class DataSourceFactory {
private static final HikariDataSource dataSource;
static {
HikariConfig config = new HikariConfig();
// Connection settings
config.setJdbcUrl("jdbc:postgresql://localhost:5432/mydb");
config.setUsername("myuser");
config.setPassword("secret");
config.setDriverClassName("org.postgresql.Driver"); // optional — auto-detected
// Pool sizing
config.setMaximumPoolSize(10); // max connections in pool
config.setMinimumIdle(2); // keep at least 2 idle connections
// Timeout settings
config.setConnectionTimeout(30_000); // ms to wait for a connection
config.setIdleTimeout(600_000); // ms idle before connection is evicted
config.setMaxLifetime(1_800_000); // ms max connection lifetime
// Pool name (appears in logs)
config.setPoolName("MyAppPool");
dataSource = new HikariDataSource(config);
}
public static HikariDataSource get() { return dataSource; }
}
Use it exactly like any other DataSource:
try (Connection conn = DataSourceFactory.get().getConnection();
PreparedStatement ps = conn.prepareStatement("SELECT count(*) FROM users");
ResultSet rs = ps.executeQuery()) {
rs.next();
System.out.println("Users: " + rs.getLong(1));
}
Key Configuration Properties
| Property | Default | Purpose |
|---|---|---|
maximumPoolSize | 10 | Hard ceiling on total connections |
minimumIdle | = maxPool | Connections kept warm when idle |
connectionTimeout | 30 000 ms | Max wait for a free connection; throws SQLTimeoutException |
idleTimeout | 600 000 ms | Time an idle connection can sit in the pool before eviction |
maxLifetime | 1 800 000 ms | Absolute max connection age: prevents stale TCP connections |
keepaliveTime | 0 (disabled) | Period to ping idle connections to keep them alive through firewalls |
connectionTestQuery | none | SQL to validate connections: use only if driver lacks JDBC 4 isValid() |
leakDetectionThreshold | 0 (disabled) | Log a warning if a connection is held longer than this (ms) |
schema | driver default | Default schema for new connections |
autoCommit | true | Initial auto-commit for borrowed connections |
Pool Sizing: The Right Formula
Bigger is not better. Every extra connection uses server memory and adds contention. The HikariCP author recommends:
# Pool size formula (PostgreSQL team research)
pool_size = (core_count * 2) + effective_spindle_count
# For a 4-core server with SSDs (spindles = 1):
pool_size = (4 * 2) + 1 = 9 → round up to 10
Connection Leak Detection
If code borrows a connection but never returns it (missing try-with-resources), the pool eventually starves. Enable leak detection to log a warning with a stack trace pointing to the offending call site:
config.setLeakDetectionThreshold(5_000); // warn if connection held > 5 s
Sample log output:
WARN HikariPool-1 - Connection leak detection triggered for
com.example.OrderService.createOrder(OrderService.java:47),
stack trace follows...
leakDetectionThreshold to a value longer than any legitimate long-running query you have. Setting it too low creates false positives for batch jobs.Configuration via hikari.properties File
Instead of code, you can configure HikariCP from a properties file on the classpath:
# src/main/resources/hikari.properties
dataSourceClassName=org.postgresql.ds.PGSimpleDataSource
dataSource.serverName=localhost
dataSource.portNumber=5432
dataSource.databaseName=mydb
dataSource.user=myuser
dataSource.password=secret
maximumPoolSize=10
minimumIdle=2
connectionTimeout=30000
idleTimeout=600000
maxLifetime=1800000
poolName=MyAppPool
HikariConfig config = new HikariConfig("/hikari.properties");
HikariDataSource ds = new HikariDataSource(config);
Graceful Shutdown
Close the pool when the application exits to release all connections cleanly:
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
if (!DataSourceFactory.get().isClosed()) {
DataSourceFactory.get().close();
System.out.println("Connection pool closed");
}
}));