Блог → JDBC Connection pool - своими руками. Часть 1

Как известно, технология распределенного программирования на основе сервлетов и JSP-страниц позволяет динамически формировать содержание интернет-сайтов при помощи информации, хранящейся в базах данных. Очень часто программисты не уделяют должного внимания скорости соединения и взаимодействию с базой данных в проектируемых ими программах. Так, на каждый запрос от пользователя создается соединение с базой данных, на что тратятся дополнительные ресурсы и время. Если же выполняются короткие запросы, то порой процесс поиска в базе данных может оказаться значительно короче, чем открытие самого соединения. Использование ограниченного количества обращений к базе данных для всех запросов от пользователей позволяет в значительной степени улучшить производительность web-сервера и самой базы данных. В связи с тем, что технология сервлетов позволяет программистам хранить информацию между запросами от пользователей, использование пула соединений может решить проблему быстродействия современных, динамических интернет-сайтов.

По сути, сама технология сервлетов достаточно проста. Инициализация сервлета-объекта проводится только один раз при загрузке сервлета в контейнер сервера. В этом случае программисты могут воспользоваться методом init() для инициализации глобальных переменных. Далее, когда клиент вызывает сервлет через его URL-адрес, контейнер перехватывает этот вызов и перенаправляет его в метод экземпляра сервлета service(), формируя HTTP-ответ, основанный на данных запроса, в соответствующих методах. В перерывах между запросами экземпляр сервлета находится в резидентном состоянии, т.е. не уничтожается, и может хранить любую доступную информацию в своем контексте в виде переменных экземпляра, к которым относится и пул соединений. При удалении сервлета из памяти контейнера выполняется метод destroy().

Для доступа к реляционным базам данных в java используется технология JDBC. Для того чтобы выполнить SQL-запрос, получить данные из базы или изменить их, необходимо открыть соединение к базе данных и получить объект класса Connection, на основе которого и выполняются любые действия с базой. Типичная последовательность действий при работе с JDBC выглядит примерно следующим образом:
• загрузить драйвер базы данных в память виртуальной машины;
• получить соединение (Connection) с базой данных;
• подготовить выражение-запрос (Statement);
• выполнить запрос;
• разобрать полученные данные (ResultSet).

Простейший пул соединений представляет собой класс, объединяющий набор объектов JDBC Connection и методы доступа к ним. По своей сути это - контейнер с простейшим интерфейсом, который реализует механизм управления соединениями с базой данных. Размер такого хранилища определяется программистом и может варьироваться в зависимости от предоставленных ресурсов. В статье рассмотрен вариант использования простого пула соединений на основе конечного класса -DBConnectionPool, который состоит из набора методов для организации соединений. Рассматриваемый пул соединений дает возможность:
• загрузить необходимые драйверы для конкретной базы данных;
• получить ссылку на объект типа DBConnectionPool, который представляет собой непосредственно сам пул соединений;
• получить доступное соединение типа Connection из хранилища;
• возвратить соединение обратно в хранилище;
• уничтожить все ресурсы и закрыть все соединения из хранилища.

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

Класс DBConnectionPool

Класс DBConnectionPool представляет собой единое хранилище соединений с заранее выбранной базой данных. Доступ к базе данных осуществляется на основе трех основных параметров: URL-идентификатора базы данных, имени пользователя и пароля. URL-идентификатор состоит также из трех частей: идентификатора протокола (всегда jdbc), идентификатора драйвера (например, odbc, idb, oracle и т.д.) и идентификатора базы данных (зависит от реализации драйвера). Например, URL-идентификатор "jdbc:odbc:test" используется для доступа к базе данных через мост JDBC-ODBC.

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

Инициализация хранилища

Конструктор DBConnectionPool объявлен как private, что защищает его от вызовов вне собственного класса. Данное ограничение позволяет использовать в полной мере возможности паттерна "Одиночка" (Singleton), возможности использования которого будут подробно рассмотрены в контексте использования метода getInstance().

private DBConnectionPool
(String URL, String user, String password, int maxConn) {
this.URL = URL;
this.user = user;
this.password = password;
this.maxConn = maxConn;
String logFile = "DBConnection.log";

try {
log = new PrintWriter(new FileWriter(logFile, true), true);
}
catch (IOException e) {
System, err. print In ("Can't open the log file: " + logFile);
log = new PrintWriter(System.err);
}

loadDrivers(); }


В результате вызова конструктора происходит инициализация основных переменных экземпляра, создание потока вывода сообщений, а также регистрация JDBC-драйвера для конкретной базы данных:

private void loadDrivers() {

try {
Driver driver = (Driver)Class.forName (DRIVER_NAME).newlnstance{);
DriverManager.registerDriver (driver);
log("Registered JDBC driver ");
}

catch (Exception e) {
log("Can't register JDBC driver");
}

}


Статический метод класса getInstance() полностью контролирует создание экземпляра класса DBConnectionPool, предоставляя различным запросам ссылку только на один единственный объект типа DBConnectionPool. Это классическая модель одиночки (Singleton), описанная "бандой четырёх" (Е.Gamma, R.Helm, R.Johnson, J.Vlissides, "Design Patterns, Elements of Reusable Object-Oriented Software"). Именно такой подход следует использовать в тех случаях, когда создание и инициализация экземпляра требует значительных ресурсов, а результат не всегда востребован.

public static synchronized

DBConnectionPool getlnstance
(String URL, String user, String password, int maxConn) {

if (instance == null) {
instance = new DBConnectionPool(URL, user, password, maxConn);
}

return instance; }


Любопытно, не правда ли? Собственно, на этом, с вашего позволения, я сегодня хотел бы остановиться. Разумеется, будет и продолжение, но уже в следующей части статьи, предположительно - завтра! До встречи в блоге.