Connection Pooling and DataSource in JDBC

Naveen AnamaneniUpdated · 7 min read

Connection pooling is the practice of keeping a set of already-open database connections and reusing them, so the program doesn't open a new connection for every piece of work. Opening a connection is expensive: the driver must create a network connection, the database must check the user name and password, and both sides allocate memory. A connection pool opens a number of connections once, lends one to each caller that asks, and takes it back when the caller is done. In JDBC, a pool is reached through the javax.sql.DataSource interface. In this lesson, you will learn how pooling works, how to use a DataSource, which settings matter, and which pool libraries are used in practice.

How a Pool Works

StepWhat happens
1. StartupThe pool opens some connections and keeps them idle.
2. BorrowdataSource.getConnection() hands out an idle connection at once.
3. UseThe caller runs its SQL as usual.
4. ReturnThe caller calls close(). The pool does not close the real connection; it marks it idle again.
5. FullIf every connection is in use, the next caller waits, up to a timeout.

The crucial point is step 4: with a pool, close() means "give it back". Your code doesn't change at all, which is why try-with-resources works perfectly with pooled connections.

DriverManager vs DataSource

PointDriverManager.getConnectionDataSource.getConnection
Each callOpens a new connection (slow)Borrows an open connection (fast)
close()Really closes itReturns it to the pool
Limit on connectionsNone; a busy application can overwhelm the databaseA fixed maximum
ConfigurationIn the calling codeIn one place, when the DataSource is created
Used inSmall programsEvery server application

Example: A Pool in Action

H2 includes a simple pool, JdbcConnectionPool, which is enough to see the behavior. It implements DataSource.

Pool.java▶ Try it
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import javax.sql.DataSource;
import org.h2.jdbcx.JdbcConnectionPool;
 
public class Pool {
    // Application code only knows the DataSource interface.
    static int countStudents(DataSource ds) throws SQLException {
        try (Connection con = ds.getConnection();
             Statement st = con.createStatement();
             ResultSet rs = st.executeQuery("SELECT COUNT(*) FROM student")) {
            rs.next();
            return rs.getInt(1);
        }
    }
 
    public static void main(String[] args) throws Exception {
        JdbcConnectionPool pool = JdbcConnectionPool.create(
            "jdbc:h2:mem:school;DB_CLOSE_DELAY=-1", "sa", "");
        pool.setMaxConnections(3);
        try {
            try (Connection con = pool.getConnection();
                 Statement st = con.createStatement()) {
                st.execute("CREATE TABLE student (id INT, name VARCHAR(50))");
                st.executeUpdate("INSERT INTO student VALUES "
                    + "(1, 'Navika'), (2, 'Srihitha'), (3, 'Advitha')");
            }
            System.out.println("Students: " + countStudents(pool));
            System.out.println("In use after the query: "
                + pool.getActiveConnections());
 
            Connection a = pool.getConnection();
            Connection b = pool.getConnection();
            System.out.println("Borrowed two: " + pool.getActiveConnections()
                + " in use, limit " + pool.getMaxConnections());
            a.close();
            b.close();
            System.out.println("Returned them: "
                + pool.getActiveConnections() + " in use");
 
            // Borrow more than the pool has and never give them back.
            pool.setLoginTimeout(1);
            Connection[] held = new Connection[3];
            for (int i = 0; i < 3; i++) {
                held[i] = pool.getConnection();
            }
            try {
                pool.getConnection();
            } catch (SQLException e) {
                System.out.println("Pool empty: " + e.getMessage());
            }
            for (Connection c : held) {
                c.close();
            }
        } finally {
            pool.dispose();
        }
    }
}
Output
Students: 3
In use after the query: 0
Borrowed two: 2 in use, limit 3
Returned them: 0 in use
Pool empty: Login timeout

Explanation:

  1. countStudents takes a DataSource, not a pool class. It borrows a connection, uses it, and returns it through try-with-resources. This method would work unchanged with any pool library.
  2. "In use after the query: 0" shows that close() returned the connection to the pool.
  3. Borrowing two raises the in-use count to 2; closing them brings it back to 0. The real connections stay open inside the pool, ready for the next caller.
  4. The limit: the pool allows 3 connections. With all 3 borrowed and not returned, a fourth request waits for the 1-second timeout and then fails. In a real application, that error almost always means some code forgot to close a connection.

Pool Libraries

LibraryNotes
HikariCPThe most widely used pool; fast and small. The default in Spring Boot.
Apache Commons DBCP 2A long-standing pool from Apache.
c3p0An older pool, still found in existing projects.
Tomcat JDBC PoolComes with the Tomcat server.

Setting up HikariCP looks like this:

HikariSetup.java
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:postgresql://localhost:5432/school");
config.setUsername(System.getenv("DB_USER"));
config.setPassword(System.getenv("DB_PASSWORD"));
config.setMaximumPoolSize(10);
config.setConnectionTimeout(3000);   // ms to wait for a free connection
 
DataSource dataSource = new HikariDataSource(config);

After that, the rest of the application only sees DataSource.

Settings That Matter

SettingMeaningGuidance
Maximum pool sizeThe most connections the pool will openStart small (around 10); more is often slower, because the database has limited CPU and disks
Minimum idleConnections kept ready when quietOften the same as the maximum
Connection timeoutHow long a caller waits for a free connectionA few seconds; fail fast
Idle timeout, maximum lifetimeWhen unused or old connections are retiredKeep the lifetime below any timeout the database or network imposes
ValidationTesting a connection before lending itPools do this with isValid

Common Mistakes

Not closing borrowed connections. The pool empties and every caller times out. This is the classic connection leak.

A pool that is too large. Hundreds of connections make the database slower, not faster.

Creating a new pool for each request. A pool is created once, at startup, and shared.

Leaving a borrowed connection in a changed state. Restore auto-commit and end transactions before returning it; the next borrower gets the same connection.

Interview Corner

What is connection pooling and why is it used?

Connection pooling maintains a cache of open database connections that are reused instead of being created and closed for each request. Creating a connection is costly (network setup, authentication, session allocation), so reuse greatly improves response time and throughput, and the pool's maximum size protects the database from being overwhelmed. Applications obtain connections from a javax.sql.DataSource; calling close() on a pooled connection returns it to the pool. Common implementations are HikariCP, Apache DBCP and c3p0.

What is a DataSource?

javax.sql.DataSource is the standard interface for a factory of connections, the preferred alternative to DriverManager. It is configured once with the URL, credentials and pool settings, and then passed to the code that needs connections, which calls getConnection(). Implementations may be basic, pooled, or support distributed transactions.

Practice

  1. Easy: Change the pool's maximum to 5 and adjust the last part of the example so it still demonstrates an empty pool.
  2. Harder: Rewrite the StudentDao from "CRUD Operations with JDBC" to take a DataSource and borrow a connection inside each method.

Quick check: Connection pooling

4 questions
1What does close() do on a pooled connection?
2Why is opening a connection expensive?
3Every connection is in use and another is requested. What happens?
4What usually causes a pool to run out?

Key Takeaways

Was this tutorial helpful?

Lesson 500 of 578