Database connection pooling is essential for optimizing application performance by efficiently managing database connections. HikariCP, known for its speed and lightweight footprint, is a preferred choice for Java applications using JDBC. This guide will show you how to implement HikariCP for efficient connection pooling in your Java project.

Why Use HikariCP for JDBC Connection Pooling?

HikariCP offers several advantages over traditional connection pools:

  • High Performance – Faster than other JDBC connection pools like C3P0 and Apache DBCP.
  • Low Latency – Optimized to minimize connection acquisition time.
  • Auto-Tuning – Adapts dynamically to workload changes.
  • Resilience – Efficient handling of database failures.

Step 1: Add HikariCP to Your Java Project

To get started, add the HikariCP dependency in your pom.xml (Maven):


<dependency>
    <groupId>com.zaxxer</groupId>
    <artifactId>HikariCP</artifactId>
    <version>5.0.1</version> <!-- Use latest version -->
</dependency>

Step 2: Configure HikariCP in Java

Create a utility class to configure the connection pool:


import com.celac.jdbc.app.util.LocalConfigurationPropertiesLoader;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import java.util.Properties;
import javax.sql.DataSource;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

/**
 * This is utility class to configure the connection pool
 * @author scelac
 */
public class DatabaseHikariConnectionPool {
    private final static Logger logger = LogManager.getLogger(DatabaseHikariConnectionPool.class);
    private static final HikariDataSource dataSource;
    private static final Properties environment = LocalConfigurationPropertiesLoader.getInstance();

    static {
        HikariConfig config = new HikariConfig();
        config.setJdbcUrl(prepareDataSourceURL()); // In-memory DB
        config.setUsername(environment.getProperty("datasource.username"));
        config.setPassword(environment.getProperty("datasource.password"));
        config.setMaximumPoolSize(toInt(environment.getProperty("connection.pool.max.size")));
        config.setMinimumIdle(toInt(environment.getProperty("connection.pool.min.idle")));
        config.setIdleTimeout(toInt(environment.getProperty("connection.pool.idle.timeout")));
        config.setMaxLifetime(toInt(environment.getProperty("connection.pool.max.life.time")));
        config.setConnectionTimeout(toInt(environment.getProperty("connection.pool.conn.timeout")));
        dataSource = new HikariDataSource(config);
    }

    /**
     *  This method returns preconfigured datasource
     * @return DataSource
     */
    public static DataSource getDataSource() {
        return dataSource;
    }

    private static int toInt(String val) {
        return Integer.parseInt(val);
    }
    private static String prepareDataSourceURL() {
        String dataSourceURL = environment.getProperty("datasource.url.template")
                .replace("URL", environment.getProperty("datasource.url"))
                .replace("PORT", environment.getProperty("datasource.port"))
                .replace("DB_NAME", environment.getProperty("datasource.name"));
        return dataSourceURL;
    }

}
 

Step 3: Config loading connection  parameters from application.properties file

This step is optional, but in my code example from step 2 is used, I prefer to keep all configuration parameters in a separate file. 

location of application properties file



datasource.url.template=jdbc:mysql://URL:PORT/DB_NAME?serverTimezone=UTC
datasource.url=localhost
datasource.port=3306
datasource.name=lessons_test
datasource.username=root
datasource.password=
connection.pool.max.size=10
connection.pool.min.idle=2
connection.pool.idle.timeout=30000
connection.pool.max.life.time=1800000
connection.pool.conn.timeout=3000

Also, we need to create a mechanism for reading and loading these parameters from application.properties file 


import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Properties;

public class LocalConfigurationPropertiesLoader {
  private static final Logger logger =
      LogManager.getLogger(LocalConfigurationPropertiesLoader.class);
  private static Properties properties;

  private LocalConfigurationPropertiesLoader() {}

  public static Properties getInstance() {
    if (properties == null) {
      properties = loadProperties();
      return properties;
    }
    return properties;
  }

  private static Properties loadProperties() {
    logger.info("Start load Properties");
    final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
    final Path propertyFile = Paths.get("application.properties");
    Properties appProps = new Properties();
    logger.info("Load Properties from file" + propertyFile);
    try (InputStream stream = classLoader.getResourceAsStream("application.properties")) {
      appProps.load(stream);
      printToSystemOutput(appProps);
    } catch (IOException e) {
      logger.error("Exception: " + e);
    }

    return appProps;
  }

  private static void printToSystemOutput(Properties config) throws IOException {
    config.store(System.out, "Loaded properties:");
  }
}

Step 3: Using the Connection Pool in a DAO

Now, integrate the connection pool with a simple Data Access Object (DAO) for more details on how to do that follow the link:  Implement DAO pattern in java

Step 4: Verify the Connection Pool

Create a main class to verify that the connection pooling works as expected:


public class MainJDBCApp {
    private final static Logger logger = LogManager.getLogger(MainJDBCApp.class);
    public static void main(String[] args) throws IOException {

    UserDao userDao = new UserDaoImpl(DatabaseHikariConnectionPool.getDataSource());
    User user = userDao.findOne(1l);
    logger.info(user);
    
  }
}


Best Practices for HikariCP Connection Pooling

To ensure optimal performance, follow these best practices:

  • Set maximumPoolSize based on the application load – A reasonable default is 10 connections.
  • Tune idleTimeout and maxLifetime – Prevents stale connections from staying too long.
  • Use connectionTimeout wisely – Avoid long wait times when all connections are busy.
  • Monitor with metrics – Use tools like Micrometer for real-time insights.

Conclusion

Implementing HikariCP for JDBC connection pooling significantly enhances application performance, reduces database load, and ensures efficient connection management. By following this guide, you can easily integrate HikariCP into your Java application and enjoy its speed and reliability.

For more database performance optimization tips, stay tuned!