Skip to content
AITroveRead. Build. Understand.
Make this comfortable

Java connection pools: borrowed handles, rollback and admission timeout

Last updated: 29 Sept 20264 min read
tutorial
IntermediateBy AITrove Editorial

A connection pool lends logical connection handles over a bounded collection of physical database connections; closing a borrowed handle ordinarily returns it to the pool.

Download Java source kit

Java 17+. The downloadable Maven fixture supplies HikariCP 5.1.0 and H2 2.3.232.

Keep the transaction with the borrower

The pool below has one maximum connection and a finite acquisition timeout. A caller owns the borrowed handle only for its operation. The handle must be closed on every path so another caller can borrow capacity; closing it is different from shutting down the pool itself.

The fixture disables auto-commit, creates its table, starts a data change and rolls that change back. A later borrower sees zero rows. That check is about transaction cleanup, not a throughput claim. The code calls rollback explicitly rather than depending on implicit pool cleanup to define business behavior.

The program also holds the only connection while attempting a second borrow. The timed rejection verifies that saturation is visible as an exception, rather than an unbounded wait. A larger pool does not automatically improve database throughput; it can move contention into the server.

Budget admission and the admitted operation

Connection acquisition timeout is not a statement execution timeout or a complete request deadline. A service needs a policy for each stage and a shutdown owner for the pool. Monitor active, waiting and timed-out borrowers under representative workload before choosing a production capacity.

Working program

Java
import com.zaxxer.hikari.*;
import java.sql.*;
public class PooledReceiptBoundary {
    public static void main(String[] args)throws Exception{
        HikariConfig config=new HikariConfig();config.setJdbcUrl("jdbc:h2:mem:pool_"+java.util.UUID.randomUUID()+";DB_CLOSE_DELAY=-1");config.setMaximumPoolSize(1);config.setMinimumIdle(0);config.setConnectionTimeout(300);config.setAutoCommit(false);
        try(HikariDataSource pool=new HikariDataSource(config)){
            try(Connection connection=pool.getConnection()){
                try(Statement sql=connection.createStatement()){sql.execute("CREATE TABLE receipts(id INT PRIMARY KEY)");}
                try(PreparedStatement insert=connection.prepareStatement("INSERT INTO receipts VALUES(?)")){insert.setInt(1,17);insert.executeUpdate();}connection.rollback();
            }
            try(Connection held=pool.getConnection()){
                try(Statement query=held.createStatement();ResultSet rows=query.executeQuery("SELECT COUNT(*) FROM receipts")){rows.next();System.out.println(rows.getInt(1));}
                try(Connection unexpected=pool.getConnection()){throw new IllegalStateException("Second borrow admitted");}
                catch(SQLTransientConnectionException saturated){System.out.println("pool timeout");}held.rollback();
            }
            try(Connection again=pool.getConnection()){System.out.println(again.isValid(1));again.rollback();}
        }
    }
}

Output

Output
0
pool timeout
true

Costs and boundaries

The pool retains at most the configured physical connections, but each connection also has driver and server-side state. Saturated acquisition waits up to its configured policy, subject to scheduling. This H2 fixture checks borrow/return, rollback and saturation; it does not size a production database pool.

Common Mistakes

  • Closing a borrowed handle is not the same as closing the pool.
  • Acquisition timeout does not bound the SQL statement.
  • Do not retain a borrowed connection while waiting for unrelated work.

Read next

Session visibility, Admission accounting.

Apply this contract in Spring

Spring JdbcTemplate: bound values and visible database constraints. These lessons keep framework assembly separate from the Java contract.

java
connection-pool
Storage details