Java connection pool getconnection

Простое руководство по пулу соединений в Java

Пул соединений является хорошо известным шаблоном доступа к данным, основная цель которого состоит в том, чтобы уменьшить накладные расходы, связанные с выполнением соединений с базой данных и операциями чтения / записи с базой данных.

Вкратце,a connection pool is, at the most basic level, a database connection cache implementation, который можно настроить в соответствии с конкретными требованиями.

В этом руководстве мы сделаем краткий обзор нескольких популярных фреймворков для пула соединений и узнаем, как реализовать с нуля собственный пул соединений.

2. Почему пул соединений?

Вопрос риторический, конечно.

Если мы проанализируем последовательность шагов, включенных в типичный жизненный цикл подключения к базе данных, мы поймем, почему:

  1. Открытие соединения с базой данных с помощью драйвера базы данных
  2. ОткрытиеTCP socket для чтения / записи данных
  3. Чтение / запись данных через сокет
  4. Закрытие соединения
  5. Закрытие розетки

Становится очевидным, чтоdatabase connections are fairly expensive operations, и как таковой, следует свести к минимуму во всех возможных случаях использования (в крайних случаях просто избегать).

Здесь в игру вступают реализации пула соединений.

Просто внедрив контейнер соединений с базой данных, который позволяет нам повторно использовать несколько существующих соединений, мы можем эффективно сэкономить затраты на выполнение огромного количества дорогостоящих поездок по базам данных, тем самым повышая общую производительность наших приложений на основе баз данных.

Читайте также:  Decoding base64 string java

3. Фреймворки пула соединений JDBC

С прагматической точки зрения, реализация пула соединений с нуля просто бессмысленна, учитывая количество доступных для предприятия инфраструктур пула соединений.

Из дидактического, что и является целью данной статьи, это не так.

Тем не менее, прежде чем мы узнаем, как реализовать базовый пул соединений, давайте сначала продемонстрируем несколько популярных фреймворков для пулов соединений.

3.1. Apache Commons DBCP

Давайте начнем этот краткий обзор сApache Commons DBCP Component, полнофункциональной платформы JDBC для пула соединений:

public class DBCPDataSource < private static 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() < >>

В этом случае мы использовали класс-оболочку со статическим блоком, чтобы легко настроить свойства DBCP.

Вот как получить объединенное соединение с классомDBCPDataSource:

Connection con = DBCPDataSource.getConnection();

3.2. HikariCP

Двигаясь дальше, давайте посмотрим наHikariCP, молниеносную структуру пула соединений JDBC, созданнуюBrett Wooldridge (для получения полной информации о том, как настроить и получить максимальную отдачу от HikariCP, проверьтеthis article ):

public class HikariCPDataSource < private static HikariConfig config = new HikariConfig(); private static 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()<> >

Аналогичным образом, вот как получить объединенное соединение с классомHikariCPDataSource:

Connection con = HikariCPDataSource.getConnection();

3.3. C3PO

Последний в этом обзоре -C3PO, мощный фреймворк JDBC4 для соединений и пулов операторов, разработанный Стивом Уолдманом:

public class C3poDataSource < private static 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) < // handle the exception >> public static Connection getConnection() throws SQLException < return cpds.getConnection(); >private C3poDataSource()<> >

Как и ожидалось, получение объединенного соединения с классомC3poDataSource аналогично предыдущим примерам:

Connection con = C3poDataSource.getConnection();

4. Простая реализация

Чтобы лучше понять основную логику пула соединений, давайте создадим простую реализацию.

Давайте начнем со слабосвязанного дизайна, основанного всего на одном интерфейсе:

public interface ConnectionPool

ИнтерфейсConnectionPool определяет общедоступный API базового пула соединений.

Теперь давайте создадим реализацию, которая предоставляет некоторые базовые функции, включая получение и освобождение объединенного соединения:

public class BasicConnectionPool implements ConnectionPool < private String url; private String user; private String password; private ListconnectionPool; private List usedConnections = new ArrayList<>(); private static int INITIAL_POOL_SIZE = 10; public static BasicConnectionPool create( String url, String user, String password) throws SQLException < Listpool = 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); > // standard constructors @Override public Connection getConnection() < 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(); >// standard getters >

Хотя это довольно наивно, классBasicConnectionPool обеспечивает минимальную функциональность, которую мы ожидаем от типичной реализации пула соединений.

Вкратце, класс инициализирует пул соединений на основеArrayList, в котором хранится 10 соединений, которые можно легко использовать повторно.

It’s possible to create JDBC connections with the DriverManager class and with Datasource implementations.

Поскольку гораздо лучше сохранить независимость создания базы данных соединений от базы данных, мы использовали первое в рамках статического метода фабрикиcreate().

В этом случае мы поместили метод вBasicConnectionPool, потому что это единственная реализация интерфейса.

В более сложном дизайне с несколькими реализациямиConnectionPool было бы предпочтительнее разместить его в интерфейсе, чтобы получить более гибкий дизайн и больший уровень согласованности.

Здесь наиболее важно подчеркнуть, что после создания пулаconnections are fetched from the pool, so there’s no need to create new ones.

Кроме того,when a connection is released, it’s actually returned back to the pool, so other clients can reuse it.

Никакого дальнейшего взаимодействия с базовой базой данных, например явного вызова методаConnection’s close(), не происходит.

5. Использование классаBasicConnectionPool

Как и ожидалось, использовать наш классBasicConnectionPool просто.

Давайте создадим простой модульный тест и получим объединенное соединение in-memoryH2:

@Test public whenCalledgetConnection_thenCorrect()

6. Дальнейшие улучшения и рефакторинг

Конечно, есть много возможностей для настройки / расширения текущей функциональности нашей реализации пула соединений.

Например, мы могли бы провести рефакторинг методаgetConnection() и добавить поддержку максимального размера пула. Если все доступные соединения заняты, а текущий размер пула меньше настроенного максимума, метод создаст новое соединение:

@Override public Connection getConnection() throws SQLException < if (connectionPool.isEmpty()) < 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; >

Обратите внимание, что метод теперь выдаетSQLException, то есть нам также нужно обновить подпись интерфейса.

Или мы можем добавить метод для корректного завершения работы нашего экземпляра пула соединений:

public void shutdown() throws SQLException < usedConnections.forEach(this::releaseConnection); for (Connection c : connectionPool) < c.close(); >connectionPool.clear(); >

В готовых к реализации реализациях пул соединений должен предоставлять ряд дополнительных функций, таких как возможность отслеживания соединений, которые используются в настоящее время, поддержка подготовленного пула операторов и т. Д.

Чтобы упростить задачу, мы опустим, как реализовать эти дополнительные функции, и для ясности оставим реализацию небезопасной для потоков.

7. Заключение

В этой статье мы подробно рассмотрели, что такое пул соединений, и узнали, как реализовать собственную реализацию пула соединений.

Конечно, нам не нужно начинать с нуля каждый раз, когда мы хотим добавить в наши приложения полнофункциональный уровень пула соединений.

Вот почему мы сначала сделали простой обзор, показывающий некоторые из самых популярных фреймворков пула соединений, чтобы мы могли иметь четкое представление о том, как с ними работать, и выбрать ту, которая лучше всего соответствует нашим требованиям.

Как обычно, доступны все примеры кода, показанные в этой статьеover on GitHub.

Источник

Простейший Connection pool без DataSource в Java

Ни для кого не секрет, что в Java EE Connection Pool реализуется используя Data Source. С примером реализации в Apache Tomcat можно ознакомиться по этой ссылке: habrahabr.ru/post/101342. Но что делать, если мы используем только Java SE и нам нужно организовать многопоточный доступ к базе данных по схеме Connection Pool. Ведь сервера приложений у нас в данном случае нет, следовательно, использовать Data Source мы не можем, а для создания соединений к бд нам придется скорее всего использовать java.sql.DriverManager. Можно еще использовать в замену данному подходу что-нибудь из этой статьи: docs.oracle.com/javase/jndi/tutorial/ldap/connect/pool.html. Скорее всего кем-то данный подход уже реализовывался на Java, по крайней мере я не обнаружил такую реализацию в сети. И я решил изобрести свой велосипед. Было бы хорошо, если в поисковике при поиске Connection pool в Java выходила ссылка на эту статью, дополненную и отредактированную по комментариям и дополнениям от habr.ru, и эта статья была бы полезна кому-нибудь. Если стало интересно, то прошу под кат.

Реализуем все в виде класса, конструктор которого принимает на вход начальное количество соединений в пуле и параметры подключения к бд (название загружаемого класса из драйвера и строка подключения вместе с именем пользователя и паролем). Назовем класс ConnectionPool, его конструктор будет иметь вид:

public ConnectionPool(String url, String driver, int initConnCnt) < try < Class.forName(driver); >catch (Exception e) < e.printStackTrace(); >this.url = url; for (int i = 0; i < initConnCnt; i++) < availableConns.addElement(getConnection()); >> 

url — это строка подключения, для MS SQL Server она будет иметь вид наподобие: jdbc:sqlserver://192.168.0.1;databaseName=dbname;username=username;password=pwd. Можно разделить имя пользователя и пароль, передавать их отдельно в DriverManager. Здесь все в одном месте, по крайней мере мне так показалось удобней. Один из допустимых драйверов для работы с MS SQL Server имеет следующий вид: com.microsoft.sqlserver.jdbc.SQLServerDriver.
В данном классе должны объявляться два вектора:

private Vector availableConns = new Vector(); private Vector usedConns = new Vector(); 

Первый будет содержать список доступных для использования соединений, второй список используемых в любой момент времени.
Кроме этого определяется переменная url, которая будет хранить строку подключения к бд:

В конструкторе используется функция getConnection, которая просто создает новое подключение. Его реализация следующая:

private Connection getConnection() < Connection conn = null; try < conn = DriverManager.getConnection(url); >catch (Exception e) < e.printStackTrace(); >return conn; > 

Итак, мы имеем вектор availableConns, заполненный Connection’ами в количестве initConnCnt штук, ни один из которых пока не используется непосредственно по назначению, т.е. для доступа к бд. Теперь напишем функцию retrieve, эта функция забирает из availableConns очередной Connection и добавляет его в usedConns, затем возвращает это соединение, тем самым он становится используемым:

public synchronized Connection retrieve() throws SQLException < Connection newConn = null; if (availableConns.size() == 0) < newConn = getConnection(); >else < newConn = (Connection) availableConns.lastElement(); availableConns.removeElement(newConn); >usedConns.addElement(newConn); return newConn; > 

Логика понятна: сначала мы проверяем, есть ли свободные соединения, если нет, то мы создаем новое подключение, если есть, то мы извлекаем последний элемент из availableConns и удаляем его из вектора свободных соединений. Затем мы только что созданное соединение или извлеченное из списка свободных добавляем в список используемых строкой

usedConns.addElement(newConn); 

и возвращаем это соединение. Конечно же без synchronized не обойтись. Как же иначе? Доступ то многопоточный, вдруг двум потокам выделится одно и то же соединение! Когда соединение становится не нужным, то мы выполняем обратную операцию, иначе говоря putback:

public synchronized void putback(Connection c) throws NullPointerException < if (c != null) < if (usedConns.removeElement(c)) < availableConns.addElement(c); >else < throw new NullPointerException("Connection not in the usedConns"); >> > 

Логика тоже понятна и не требует объянений.
Дальше можно по необходимости написать кучу всяких дополнительных функций, которые предоставляют доступ к дополнительной информации, например, функция для получения количества свободных соединений будет выглядеть следующим образом:

public int getAvailableConnsCnt()
import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.util.Vector; class ConnectionPool < private VectoravailableConns = new Vector(); private Vector usedConns = new Vector(); private String url; public ConnectionPool(String url, String driver, int initConnCnt) < try < Class.forName(driver); >catch (Exception e) < e.printStackTrace(); >this.url = url; for (int i = 0; i < initConnCnt; i++) < availableConns.addElement(getConnection()); >> private Connection getConnection() < Connection conn = null; try < conn = DriverManager.getConnection(url); >catch (Exception e) < e.printStackTrace(); >return conn; > public synchronized Connection retrieve() throws SQLException < Connection newConn = null; if (availableConns.size() == 0) < newConn = getConnection(); >else < newConn = (Connection) availableConns.lastElement(); availableConns.removeElement(newConn); >usedConns.addElement(newConn); return newConn; > public synchronized void putback(Connection c) throws NullPointerException < if (c != null) < if (usedConns.removeElement(c)) < availableConns.addElement(c); >else < throw new NullPointerException("Connection not in the usedConns array"); >> > public int getAvailableConnsCnt() < return availableConns.size(); >> 

Источник

Оцените статью