To configure an Apache DBCP connection pool for “plain” JDBC, you typically create a pooled DataSource once at startup, then get connections from it (and always close them to return to the pool).
Below are the two most common approaches.
1) Recommended (DBCP2): BasicDataSource (simplest)
Create and configure the pool
package org.kodejava.jdbc;
import org.apache.commons.dbcp2.BasicDataSource;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
public final class Dbcp2Example {
public static DataSource dataSource() {
BasicDataSource ds = new BasicDataSource();
ds.setUrl("jdbc:postgresql://localhost:5432/app");
ds.setUsername("<db_user>");
ds.setPassword("<db_password>");
ds.setMaxTotal(20);
ds.setMaxIdle(10);
ds.setMinIdle(2);
ds.setValidationQuery("SELECT 1");
ds.setTestOnBorrow(true);
return ds;
}
public static void main(String[] args) throws Exception {
DataSource ds = dataSource();
try (Connection c = ds.getConnection();
PreparedStatement ps = c.prepareStatement("SELECT 1");
ResultSet rs = ps.executeQuery()) {
while (rs.next()) System.out.println(rs.getInt(1));
}
}
}
Key idea: with pooling, conn.close() does not close the physical DB connection; it returns it to the pool.
2) Lower-level (more flexible): PoolingDataSource + Commons Pool
This approach wires DBCP to Commons Pool manually (useful when you want full control over the pool object/config).
package org.kodejava.jdbc;
import org.apache.commons.dbcp2.ConnectionFactory;
import org.apache.commons.dbcp2.DriverManagerConnectionFactory;
import org.apache.commons.dbcp2.PoolableConnection;
import org.apache.commons.dbcp2.PoolableConnectionFactory;
import org.apache.commons.dbcp2.PoolingDataSource;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import javax.sql.DataSource;
public final class ManualDbcpPoolFactory {
public static DataSource createDataSource() {
ConnectionFactory cf =
new DriverManagerConnectionFactory("jdbc:postgresql://localhost:5432/app",
"<db_user>", "<db_password>");
PoolableConnectionFactory pcf = new PoolableConnectionFactory(cf, null);
pcf.setValidationQuery("SELECT 1");
GenericObjectPoolConfig<PoolableConnection> config = new GenericObjectPoolConfig<>();
config.setMaxTotal(20);
config.setMaxIdle(10);
config.setMinIdle(2);
config.setTestOnBorrow(true);
GenericObjectPool<PoolableConnection> pool = new GenericObjectPool<>(pcf, config);
pcf.setPool(pool);
return new PoolingDataSource<>(pool);
}
}
Practical tuning checklist (what to set and why)
- Sizing
maxTotal: hard cap of concurrent borrowed connections.maxIdle/minIdle: how many connections to keep around to absorb spikes.
- Validation
- Use
validationQuery(orvalidationQueryTimeout) and pick one strategy:testOnBorrow=true(safer, slightly more overhead), ortestWhileIdle=true+ eviction run (common for reducing borrow-time latency).
- Use
- Timeouts
maxWaitMillis: how long callers wait for a free connection before failing.
- Always close
- Ensure
try-with-resourceseverywhere; leaks will exhaust the pool.
- Ensure
Maven dependencies
<dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
<version>2.14.0</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.13.1</version>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.7.7</version>
</dependency>
</dependencies>
