Connection pool java реализация

Простейший 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(); >> 

Источник

Читайте также:  Retrofit android пример на java

Пул подключений PoolConnection

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

Для разработки приложения можно использовать пулы различного типа объектов, например пул потоков Thread Pool. В данной статье рассматривается только пул подключений Connection Pool к БД.

Пул подключений необходим для WEB приложения, когда все запросы к базе данных стекаются в одно место к серверу, а создание нового подключения к БД требует значительных ресурсоов. Использование пула подключения оправдано более экономной стратегией — создание несколько подключений и слежение за их состояниям; по мере необходимости добавлять новые.

Существует несколько реализаций пулов подключений к БД. Можно, конечно, сделать и свою имплементацию. Опасность данной затеи заключается в том, что если сделать не очень аккуратно, то пул может «протекать» или объекты будут «тухнуть». Для серверной части приложения это особо неприятно — необходимо контролировать объём пула и состояние объектов.

На сегодняшний день существуют готовые решения. Самое главное научиться их правильно настраивать.

Пул подключений — apache commons dbcp, datasource

Популярный контейнер сервлетов Apache Tomcat предоставляет собственное решение для создания dbcp, основанное на библиотеке apache-commons-dbcp.

apache dbcp предполагает, что имеется некоторый набор («пул») соединений к базе данных. Когда новый пользователь запрашивает доступ к БД, ему выдаётся уже открытое соединение из этого пула. Если все открытые соединения уже заняты, создаётся новое. Как только пользователь освобождает одно из уже существующих соединений, оно становится доступно для других пользователей. Если соединение долго не используется, оно закрывается.

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

Читайте также:  Php вывод код файла

На первом этапе необходимо объявить ресурс (базу данных) в контексте приложения. Ресурс описывается следующим кодом (пример для MySQL) :

Контекст приложения представляет файл XML. Желательно хранить его в %document_root%/META-INF/context.xml, однако это не единственный вариант. Про контекст можно почитать на официальном сайте Tomcat’a: The Context Container.

Далее следует добавить ссылку на этот ресурс в дескрипторе приложения web.xml :

 DB Connection jdbc/appname javax.sql.DataSource Container  

После этого можно использовать пул подключений (connection pool java) в приложении. Для этого следует получить объект Connection в следующем коде :

InitialContext initContext= new InitialContext(); DataSource ds = (DataSource) initContext.lookup("java:comp/env/jdbc/appname"); Connection connection = ds.getConnection();

Для получения источника данных (DataSource) используется механизм JNDI (подробнее про него можно почитать здесь)

Описание пула подключений завершено, подключение в приложение получено, и теперь можно приступить к реализации логики работы с БД — connection.createStatement(). После выполнения запроса следует закрыть соединение (connection.close()). Однако в отличии от обычного соединения через драйвер JDBC, это соединение на самом деле не закроется: оно будет помечено в пуле как свободное, и его можно будет переиспользовать позже.

Перед возвратом соединения в пул все Statement и ResultSet, полученные с помощью этого соединения, автоматически закрываются в соответствии с API.

Connection Pool C3P0

Пул подключений C3P0 представлен файлом c3p0-0.9.1.2.jar (версия может отличаться). Аббревиатура расшифровывается следующим образом : Connection Pool 3.0 => СP30 => C3P0.

Принцип настройки JNDI ресурсов уже представлен выше. Необходимо определить глобальные ресурсы, а в WEB приложении указать ссылку, либо можно указать непосредственно в context.xml. Все зависит от того, где хранятся библиотеки и какая конфигурация сервера.

Пример настройки C3P0 с БД MySQL :

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

  • maxPoolSize и minPoolSize — возможные максимальное и минимальное количество подключений в пуле
  • preferredTestQuery — запрос который проверяет подключение к БД. Зависит от конкретной СУБД.
  • acquireRetryAttempts — количество попыток подключения к СУБД, если она не доступна. Ставим 0, если хотим, чтобы попытки не заканчивались.
  • testConnectionOnCheckout — «прожорлив», если можно, то лучше не использовать
Читайте также:  border-width

Задача создания пула подключений к Oracle не тривиальна. Документация приведена здесь

Пул подключений на уровне java приложения, ComboPooledDataSource

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

Следующий код демонстрирует процесс создания и инициализации пула подключений :

import java.beans.PropertyVetoException; import com.mchange.v2.c3p0.ComboPooledDataSource; . ComboPooledDataSource cpds = new ComboPooledDataSource(); try < cpds.setDriverClass("com.mysql.jdbc.Driver" ); cpds.setJdbcUrl ("jdbc:mysql://localhost:3306/dbName"); cpds.setUser ("dbLogin" ); cpds.setPassword ("dbPassword"); Properties properties = new Properties(); properties.setProperty ("user" , "dbLogin" ); properties.setProperty ("password" , "dbPassword"); properties.setProperty ("useUnicode" , "true" ); properties.setProperty ("characterEncoding", "UTF8" ); cpds.setProperties(properties); // set options cpds.setMaxStatements (180); cpds.setMaxStatementsPerConnection(180); cpds.setMinPoolSize ( 50); cpds.setAcquireIncrement ( 10); cpds.setMaxPoolSize ( 60); cpds.setMaxIdleTime ( 30); >catch (PropertyVetoException e)

Следующий код демонстрирует как получить Connection из пула, и как его закрыть (вернуть в пул):

// Получить подключение из пула try < Connection connection = cpds.getConnection(); System.out.println ("closeConnection : idleConnections = " + cpds.getNumIdleConnections() + ", busyConnections = " + cpds.getNumBusyConnections()); >catch (SQLException e) < e.printStackTrace(); >// «Вернуть» (закрыть) подключение try < connection.close(); System.out.println ("closeConnection : idleConnections = " + cpds.getNumIdleConnections() + ", busyConnections = " + cpds.getNumBusyConnections()); >catch (SQLException e)

Источник

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