Продолжаем писать сайт на java и сегодня мы поговорим о такой теме как пул соединение или connection pool на английском. Напомню, что это уже четвертая статья, посвященная простому java веб приложению. Остальные можно найти в рубрике Java web.
Коннект к базе данных требует затрат определенного времени. Особенно, если база данных находится удаленно. Если под каждых запрос делать подключение к базе, то отклик нашего приложения будет невероятно низким по скорости. Не говоря уже о ресурсах, которые оно потребит.
Во избежании таких проблем есть пул соединений, который открывает соединение только первый раз, а при закрытии соединения он не закрывает его, а отправляет в пул в случае если кто-то будет делать очередной запрос. По сути, приложение работает через специальный драйвер, который является оберткой для обычного jdbc драйвера и который имеет дополнительный функционал по работе с пулом.
Настройки для пула соединений программист может прописать вручную: количество активных соединений, время ожидания и т.д.
Для особенно активных, можно написать свой connection pool: класс, который буде иметь список соединений. У него будет переопределена функция close, которая будет возвращать соединение обратно в список и много других плюшек вроде таймера открытого соединения. Когда нет конекшина долгое время, соединение закрывается.
Мы не будем изобретать свой велосипед, а воспользуемся готовыми решениями. Так как мы используем Tomcat возьмем его реализацию пула соединений.
Для этого нужно в папке webapp создать папку META-INF и в ней создать xml файл context.xml.
Далее нужно вставить настройки нашего пула в этот файл:
<Context>
<!-- Specify a JDBC datasource -->
<Resource name="jdbc/имяПула" - можно выбрать произвольно
auth="Container"
type="javax.sql.DataSource"
username="Пользователь"
password="Пароль"
driverClassName="com.mysql.jdbc.Driver" - если используете mysql
url="jdbc:mysql://хост:3306/имяБазы?autoReconnect=true"
validationQuery="select 1"
maxActive="10"
maxIdle="4"/>
</Context>
Здесь указано по минимуму настроек. Полагаю, комментарии излишни.
Теперь осталось только использовать этот код в нашем приложении.
Создадим класс ConnectonPool и сделаем его singletone. Для тех, кто не знает: это когда нельзя создать больше одного экземпляра данного класса.
Для этого делается приватный конструктор и для доступа к экземпляру класса создается специальный метод, который возвращает только один экземпляр класса.
//private constructor
}
private static ConnectionPool instance = null;
public static ConnectionPool getInstance(){
if (instance==null)
instance = new ConnectionPool();
return instance;
}
Далее нужно создать метод, через который мы будем получать соединение, но не напрямую, а через пул соединений.
Context ctx;
Connection c = null;
try {
ctx = new InitialContext();
DataSource ds = (DataSource)ctx.lookup("java:comp/env/jdbc/имяПула");
c = ds.getConnection();
} catch (NamingException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
return c;
}
Тут нужно немного комментариев.
Context – это интерфейс, который представляет собой контекст именования, который состоит из набора привязок имени к объекту. InitialContext – класс, который является исходным контекстом для выполнения операций именования. А говоря простым языком, Context, InitialContext позволяют нам иметь доступ к службам имен и каталогов. Для любопытных советую почитать о Java Naming and Directory Interface (JNDI). Метод lookup() извлекает объект по ссылке. В нашем случае он вынимает объект и кастит его к DataSource. Таким образом мы получили DataSource, который вернет нам Connection.
Полный код:
import java.sql.SQLException;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;
public class ConnectionPool {
private ConnectionPool(){
//private constructor
}
private static ConnectionPool instance = null;
public static ConnectionPool getInstance(){
if (instance==null)
instance = new ConnectionPool();
return instance;
}
public Connection getConnection(){
Context ctx;
Connection c = null;
try {
ctx = new InitialContext();
DataSource ds = (DataSource)ctx.lookup("java:comp/env/jdbc/mydatabase");
c = ds.getConnection();
} catch (NamingException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
return c;
}
}
Это все о пуле соединений. Как всегда, видео, где я все это приконнектил к проекту и ссылка на гитхаб: https://github.com/caligula95/simplewebapp-part4
Спасибо! Как всегда просто и познавательно))
javax.naming.NameNotFoundException: Name [jdbc/warmstar] is not bound in this Context. Unable to find [jdbc].
Подскажите как эту ошибку исправить?
Вы назвали файл warmstar в папке META-INF, а пытаетесь достать файл jdbc.<Resource name="jdbc/имяПула"
ctx.lookup(“java:comp/env/jdbc/имяПула”);
Отличная статья. Кратко и по делу. Спасибо!
Спасибо за статью. Подскажите при отработке строчки ds = (DataSource) context.lookup(“java:comp/env/jdbc/mydatabase”); значение ds остается null. В дебаге выводит причину:Need to specify class name in environment or system property, or as an applet parameter, or in an application resource file: java.naming.factory.initial Что это может быть?
Попробуйте добавить строку: System.setProperty(Context.INITIAL_CONTEXT_FACTORY,
“org.apache.naming.java.javaURLContextFactory”); перед строкой ds = (DataSource) context.lookup(«java:comp/env/jdbc/mydatabase»);
Конекшн пул указанный в статье будет работать только в связке с сервером. Если Вы пытаетесь запустить данный функционал в public static void main(String[] args){
} – то есть без сервера, он будет выбрасывать ошибку, которую Вы описали.
😈
у вас неправильно написан в примере метод getInstance(). в многопоточном приложении будут ситуации, когда создается несколько пулов с таким подходом.
getInstance() должен быть потокобезопасным.