From ac801b4319d117aa10b677b354c0deb8978d3d01 Mon Sep 17 00:00:00 2001 From: Alejandro Gervasio Date: Fri, 20 Jul 2018 20:28:01 -0300 Subject: [PATCH] BAEL-1818 - A Guide to Connection Pools in Java (#4735) * Strange git issue with README.MD, wouldn't revert the file * Initial Commit * Initial Commit * Update pom.xml * Update pom.xml * Initial Commit * Update pom.xml * Update source files * Update source files * Update source files * Update source files * Update Application.java * Update pom.xml * Update HikariCPDataSourceUnitTest class * Update HikariCPDataSourceUnitTest.java * Update pom.xml * Update unit test classes * Update BasicConnectionPoolUnitTest.java * Fix indentation in DBCDDataSource class * Update DBCPDataSource.java * Update BasicConnectionPool class * Update BasicConnectionPool class * Update BasicConnectionPool.java * Update BasicConnectionPool.java * Update pom.xml * Update pom.xml * BAEL-1818 Refactored getConnection(), added shutdown(), cleaned up pom.xml * BAEL-1818 Removed getConnectionPool(), upgraded c3po version * BAEL-1818 Deleted obsolete connectionpool module * BAEL-1818 Deleted obsolete connectionpool module --- core-java/pom.xml | 36 ++++++-- .../connectionpools/BasicConnectionPool.java | 87 +++++++++++++++++++ .../connectionpools/C3poDataSource.java | 28 ++++++ .../connectionpools/ConnectionPool.java | 18 ++++ .../connectionpools/DBCPDataSource.java | 25 ++++++ .../connectionpools/HikariCPDataSource.java | 28 ++++++ .../BasicConnectionPoolUnitTest.java | 69 +++++++++++++++ .../C3poDataSourceUnitTest.java | 14 +++ .../DBCPDataSourceUnitTest.java | 14 +++ .../HikariCPDataSourceUnitTest.java | 14 +++ 10 files changed, 328 insertions(+), 5 deletions(-) create mode 100644 core-java/src/main/java/com/baeldung/connectionpool/connectionpools/BasicConnectionPool.java create mode 100644 core-java/src/main/java/com/baeldung/connectionpool/connectionpools/C3poDataSource.java create mode 100644 core-java/src/main/java/com/baeldung/connectionpool/connectionpools/ConnectionPool.java create mode 100644 core-java/src/main/java/com/baeldung/connectionpool/connectionpools/DBCPDataSource.java create mode 100644 core-java/src/main/java/com/baeldung/connectionpool/connectionpools/HikariCPDataSource.java create mode 100644 core-java/src/test/java/com/baeldung/connectionpool/BasicConnectionPoolUnitTest.java create mode 100644 core-java/src/test/java/com/baeldung/connectionpool/C3poDataSourceUnitTest.java create mode 100644 core-java/src/test/java/com/baeldung/connectionpool/DBCPDataSourceUnitTest.java create mode 100644 core-java/src/test/java/com/baeldung/connectionpool/HikariCPDataSourceUnitTest.java diff --git a/core-java/pom.xml b/core-java/pom.xml index 6ead63cfab..c52b109f72 100644 --- a/core-java/pom.xml +++ b/core-java/pom.xml @@ -1,5 +1,5 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 com.baeldung core-java @@ -73,9 +73,10 @@ org.assertj assertj-core - ${assertj.version} + ${assertj-core.version} test + commons-codec commons-codec @@ -146,6 +147,21 @@ icu4j ${icu4j.version} + + org.apache.commons + commons-dbcp2 + ${commons-dbcp2.version} + + + com.zaxxer + HikariCP + ${HikariCP.version} + + + com.mchange + c3p0 + ${c3p0.version} + @@ -301,7 +317,7 @@ - + org.apache.maven.plugins maven-javadoc-plugin @@ -348,6 +364,7 @@ org.codehaus.mojo exec-maven-plugin + ${exec-maven-plugin.version} run-benchmarks @@ -375,6 +392,7 @@ + 2.8.5 2.8.2 @@ -395,16 +413,23 @@ 0.9.0 - 3.6.1 + 3.10.0 + + 2.4.0 + 3.2.0 + 0.9.5.2 + 2.19.1 4.3.4.RELEASE 1.5.8.RELEASE + 1.1 1.4.197 2.1.0.1 1.19 + 1.19 3.0.0-M1 1.5.0-b01 @@ -412,7 +437,8 @@ 1.4.4 3.1.1 2.0.3.RELEASE + 1.6.0 61.1 - \ No newline at end of file + diff --git a/core-java/src/main/java/com/baeldung/connectionpool/connectionpools/BasicConnectionPool.java b/core-java/src/main/java/com/baeldung/connectionpool/connectionpools/BasicConnectionPool.java new file mode 100644 index 0000000000..243ec88eb5 --- /dev/null +++ b/core-java/src/main/java/com/baeldung/connectionpool/connectionpools/BasicConnectionPool.java @@ -0,0 +1,87 @@ +package com.baeldung.connectionpool.connectionpools; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +public class BasicConnectionPool implements ConnectionPool { + + private final String url; + private final String user; + private final String password; + private final List connectionPool; + private final List usedConnections = new ArrayList<>(); + private static final int INITIAL_POOL_SIZE = 10; + private final int MAX_POOL_SIZE = 20; + + public static BasicConnectionPool create(String url, String user, String password) throws SQLException { + List pool = new ArrayList<>(INITIAL_POOL_SIZE); + for (int i = 0; i < INITIAL_POOL_SIZE; i++) { + pool.add(createConnection(url, user, password)); + } + return new BasicConnectionPool(url, user, password, pool); + } + + private BasicConnectionPool(String url, String user, String password, List connectionPool) { + this.url = url; + this.user = user; + this.password = password; + this.connectionPool = connectionPool; + } + + @Override + public Connection getConnection() throws SQLException { + if (connectionPool.size() == 0) { + if (usedConnections.size() < MAX_POOL_SIZE) { + connectionPool.add(createConnection(url, user, password)); + } else { + throw new RuntimeException("Maximum pool size reached, no available connections!"); + } + } + + Connection connection = connectionPool.remove(connectionPool.size() - 1); + usedConnections.add(connection); + return connection; + } + + @Override + public boolean releaseConnection(Connection connection) { + connectionPool.add(connection); + return usedConnections.remove(connection); + } + + private static Connection createConnection(String url, String user, String password) throws SQLException { + return DriverManager.getConnection(url, user, password); + } + + public int getSize() { + return connectionPool.size() + usedConnections.size(); + } + + @Override + public String getUrl() { + return url; + } + + @Override + public String getUser() { + return user; + } + + @Override + public String getPassword() { + return password; + } + + public void shutdown() throws SQLException { + for (Connection c : usedConnections) { + this.releaseConnection(c); + } + for (Connection c : connectionPool) { + c.close(); + } + connectionPool.clear(); + } +} diff --git a/core-java/src/main/java/com/baeldung/connectionpool/connectionpools/C3poDataSource.java b/core-java/src/main/java/com/baeldung/connectionpool/connectionpools/C3poDataSource.java new file mode 100644 index 0000000000..5b91f707a9 --- /dev/null +++ b/core-java/src/main/java/com/baeldung/connectionpool/connectionpools/C3poDataSource.java @@ -0,0 +1,28 @@ +package com.baeldung.connectionpool.connectionpools; + +import com.mchange.v2.c3p0.ComboPooledDataSource; +import java.beans.PropertyVetoException; +import java.sql.Connection; +import java.sql.SQLException; + +public class C3poDataSource { + + private static final ComboPooledDataSource cpds = new ComboPooledDataSource(); + + static { + try { + cpds.setDriverClass("org.h2.Driver"); + cpds.setJdbcUrl("jdbc:h2:mem:test"); + cpds.setUser("user"); + cpds.setPassword("password"); + } catch (PropertyVetoException e) { + e.printStackTrace(); + } + } + + public static Connection getConnection() throws SQLException { + return cpds.getConnection(); + } + + private C3poDataSource(){} +} \ No newline at end of file diff --git a/core-java/src/main/java/com/baeldung/connectionpool/connectionpools/ConnectionPool.java b/core-java/src/main/java/com/baeldung/connectionpool/connectionpools/ConnectionPool.java new file mode 100644 index 0000000000..3d5ad06c3d --- /dev/null +++ b/core-java/src/main/java/com/baeldung/connectionpool/connectionpools/ConnectionPool.java @@ -0,0 +1,18 @@ +package com.baeldung.connectionpool.connectionpools; + +import java.sql.Connection; +import java.sql.SQLException; +import java.util.List; + +public interface ConnectionPool { + + Connection getConnection() throws SQLException; + + boolean releaseConnection(Connection connection); + + String getUrl(); + + String getUser(); + + String getPassword(); +} \ No newline at end of file diff --git a/core-java/src/main/java/com/baeldung/connectionpool/connectionpools/DBCPDataSource.java b/core-java/src/main/java/com/baeldung/connectionpool/connectionpools/DBCPDataSource.java new file mode 100644 index 0000000000..2f33cde883 --- /dev/null +++ b/core-java/src/main/java/com/baeldung/connectionpool/connectionpools/DBCPDataSource.java @@ -0,0 +1,25 @@ +package com.baeldung.connectionpool.connectionpools; + +import java.sql.Connection; +import java.sql.SQLException; +import org.apache.commons.dbcp2.BasicDataSource; + +public class DBCPDataSource { + + private static final BasicDataSource ds = new BasicDataSource(); + + static { + ds.setUrl("jdbc:h2:mem:test"); + ds.setUsername("user"); + ds.setPassword("password"); + ds.setMinIdle(5); + ds.setMaxIdle(10); + ds.setMaxOpenPreparedStatements(100); + } + + public static Connection getConnection() throws SQLException { + return ds.getConnection(); + } + + private DBCPDataSource(){} +} diff --git a/core-java/src/main/java/com/baeldung/connectionpool/connectionpools/HikariCPDataSource.java b/core-java/src/main/java/com/baeldung/connectionpool/connectionpools/HikariCPDataSource.java new file mode 100644 index 0000000000..5ed2de181d --- /dev/null +++ b/core-java/src/main/java/com/baeldung/connectionpool/connectionpools/HikariCPDataSource.java @@ -0,0 +1,28 @@ +package com.baeldung.connectionpool.connectionpools; + +import com.zaxxer.hikari.HikariConfig; +import com.zaxxer.hikari.HikariDataSource; +import java.sql.Connection; +import java.sql.SQLException; + +public class HikariCPDataSource { + + private static final HikariConfig config = new HikariConfig(); + private static final HikariDataSource ds; + + static { + config.setJdbcUrl("jdbc:h2:mem:test"); + config.setUsername("user"); + config.setPassword("password"); + config.addDataSourceProperty("cachePrepStmts", "true"); + config.addDataSourceProperty("prepStmtCacheSize", "250"); + config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048"); + ds = new HikariDataSource(config); + } + + public static Connection getConnection() throws SQLException { + return ds.getConnection(); + } + + private HikariCPDataSource(){} +} diff --git a/core-java/src/test/java/com/baeldung/connectionpool/BasicConnectionPoolUnitTest.java b/core-java/src/test/java/com/baeldung/connectionpool/BasicConnectionPoolUnitTest.java new file mode 100644 index 0000000000..5edc6bba94 --- /dev/null +++ b/core-java/src/test/java/com/baeldung/connectionpool/BasicConnectionPoolUnitTest.java @@ -0,0 +1,69 @@ +package com.baeldung.connectionpool; + +import com.baeldung.connectionpool.connectionpools.BasicConnectionPool; +import com.baeldung.connectionpool.connectionpools.ConnectionPool; +import java.sql.Connection; +import java.sql.SQLException; +import java.util.List; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import org.junit.BeforeClass; +import org.junit.Test; + +public class BasicConnectionPoolUnitTest { + + private static ConnectionPool connectionPool; + + @BeforeClass + public static void setUpBasicConnectionPoolInstance() throws SQLException { + connectionPool = BasicConnectionPool.create("jdbc:h2:mem:test", "user", "password"); + } + + @Test + public void givenBasicConnectionPoolInstance_whenCalledgetConnection_thenCorrect() throws Exception { + assertTrue(connectionPool.getConnection().isValid(1)); + } + + @Test + public void givenBasicConnectionPoolInstance_whenCalledreleaseConnection_thenCorrect() throws Exception { + Connection connection = connectionPool.getConnection(); + assertThat(connectionPool.releaseConnection(connection)).isTrue(); + } + + @Test + public void givenBasicConnectionPoolInstance_whenCalledgetUrl_thenCorrect() { + assertThat(connectionPool.getUrl()).isEqualTo("jdbc:h2:mem:test"); + } + + @Test + public void givenBasicConnectionPoolInstance_whenCalledgetUser_thenCorrect() { + assertThat(connectionPool.getUser()).isEqualTo("user"); + } + + @Test + public void givenBasicConnectionPoolInstance_whenCalledgetPassword_thenCorrect() { + assertThat(connectionPool.getPassword()).isEqualTo("password"); + } + + @Test(expected = RuntimeException.class) + public void givenBasicConnectionPoolInstance_whenAskedForMoreThanMax_thenError() throws Exception { + // this test needs to be independent so it doesn't share the same connection pool as other tests + ConnectionPool cp = BasicConnectionPool.create("jdbc:h2:mem:test", "user", "password"); + final int MAX_POOL_SIZE = 20; + for (int i = 0; i < MAX_POOL_SIZE + 1; i++) { + cp.getConnection(); + } + fail(); + } + + @Test + public void givenBasicConnectionPoolInstance_whenSutdown_thenEmpty() throws Exception { + ConnectionPool cp = BasicConnectionPool.create("jdbc:h2:mem:test", "user", "password"); + assertThat(((BasicConnectionPool)cp).getSize()).isEqualTo(10); + + ((BasicConnectionPool) cp).shutdown(); + assertThat(((BasicConnectionPool)cp).getSize()).isEqualTo(0); + } +} diff --git a/core-java/src/test/java/com/baeldung/connectionpool/C3poDataSourceUnitTest.java b/core-java/src/test/java/com/baeldung/connectionpool/C3poDataSourceUnitTest.java new file mode 100644 index 0000000000..a02daa40f6 --- /dev/null +++ b/core-java/src/test/java/com/baeldung/connectionpool/C3poDataSourceUnitTest.java @@ -0,0 +1,14 @@ +package com.baeldung.connectionpool; + +import com.baeldung.connectionpool.connectionpools.C3poDataSource; +import java.sql.SQLException; +import static org.junit.Assert.assertTrue; +import org.junit.Test; + +public class C3poDataSourceUnitTest { + + @Test + public void givenC3poDataSourceClass_whenCalledgetConnection_thenCorrect() throws SQLException { + assertTrue(C3poDataSource.getConnection().isValid(1)); + } +} \ No newline at end of file diff --git a/core-java/src/test/java/com/baeldung/connectionpool/DBCPDataSourceUnitTest.java b/core-java/src/test/java/com/baeldung/connectionpool/DBCPDataSourceUnitTest.java new file mode 100644 index 0000000000..9583eedf4b --- /dev/null +++ b/core-java/src/test/java/com/baeldung/connectionpool/DBCPDataSourceUnitTest.java @@ -0,0 +1,14 @@ +package com.baeldung.connectionpool; + +import com.baeldung.connectionpool.connectionpools.DBCPDataSource; +import java.sql.SQLException; +import static org.junit.Assert.assertTrue; +import org.junit.Test; + +public class DBCPDataSourceUnitTest { + + @Test + public void givenDBCPDataSourceClass_whenCalledgetConnection_thenCorrect() throws SQLException { + assertTrue(DBCPDataSource.getConnection().isValid(1)); + } +} \ No newline at end of file diff --git a/core-java/src/test/java/com/baeldung/connectionpool/HikariCPDataSourceUnitTest.java b/core-java/src/test/java/com/baeldung/connectionpool/HikariCPDataSourceUnitTest.java new file mode 100644 index 0000000000..6b78815797 --- /dev/null +++ b/core-java/src/test/java/com/baeldung/connectionpool/HikariCPDataSourceUnitTest.java @@ -0,0 +1,14 @@ +package com.baeldung.connectionpool; + +import com.baeldung.connectionpool.connectionpools.HikariCPDataSource; +import java.sql.SQLException; +import static org.junit.Assert.assertTrue; +import org.junit.Test; + +public class HikariCPDataSourceUnitTest { + + @Test + public void givenHikariDataSourceClass_whenCalledgetConnection_thenCorrect() throws SQLException { + assertTrue(HikariCPDataSource.getConnection().isValid(1)); + } +} \ No newline at end of file