Pull to refresh

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

Reading time4 min
Views38K
Ни для кого не секрет, что в 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<Connection> availableConns = new Vector<Connection>();
private Vector<Connection> usedConns = new Vector<Connection>();

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

private String 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() {
		return availableConns.size();
	}

Полностью весь класс:
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Vector;

class ConnectionPool {
	private Vector<Connection> availableConns = new Vector<Connection>();
	private Vector<Connection> usedConns = new Vector<Connection>();
	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();
	}
}

upd. изменил putback согласно комментарию.
Tags:
Hubs:
-1
Comments35

Articles

Change theme settings