Лучший способ управления подключением к базе данных для сервлета Java
Каков наилучший способ управления соединением базы данных в сервлете Java?
В настоящее время я просто открываю соединение в функции init()
, а затем закрываю его в destroy()
.
Однако я обеспокоен тем, что "постоянное" подключение к базе данных может быть плохой.
Это правильный способ справиться с этим? Если нет, то какие варианты лучше?
edit: дать немного больше разъяснений: я попытался просто открыть/закрыть новое соединение для каждого запроса, но при тестировании я видел проблемы с производительностью из-за создания слишком большого количества подключений.
Есть ли какое-либо значение при совместном использовании соединения по нескольким запросам? Запросы для этого приложения почти все "доступны только для чтения" и поступают довольно быстро (хотя запрошенные данные довольно малы).
Ответы
Ответ 1
Я действительно не согласен с использованием Commons DBCP. Вам действительно нужно отложить до контейнера управление пулом соединений для вас.
Поскольку вы используете Java Servlets, это подразумевает запуск в контейнере Servlet и все основные контейнеры Servlet, с которыми я знаком, для управления пулами соединений (спецификация Java EE может даже потребовать его). Если в вашем контейнере используется DBCP (как это делает Tomcat), отлично, в противном случае просто используйте то, что предоставляет ваш контейнер.
Ответ 2
Как все говорят, вам нужно использовать пул соединений. Зачем? Что? Etc.
Что не так с вашим решением
Я знаю это, так как я также думал, что это была хорошая идея когда-то. Проблема двояка:
- Все потоки (запросы сервлетов, которые подаются с одним потоком на каждый) будут иметь одно и то же соединение. Поэтому запросы будут обрабатываться по одному за раз. Это очень медленно, даже если вы просто сидите в одном браузере и опираетесь на клавишу F5. Попробуйте: этот материал звучит высокоуровневым и абстрактным, но он эмпирический и проверяемый.
- Если соединение по какой-либо причине прерывается, метод init не будет вызываться снова (потому что сервлет не будет выведен из работы). Не пытайтесь справиться с этой проблемой, поставив try-catch в doGet или doPost, потому что тогда вы окажетесь в аду (вроде написания сервера приложений без запроса).
- В отличие от того, что можно подумать, у вас не будет проблем с транзакциями, поскольку начало транзакции связано с потоком, а не только с соединением. Возможно, я ошибаюсь, но так как это плохое решение, не потейте.
Почему пул соединений
Пулы соединений дают вам целую кучу преимуществ, но больше всего они решают проблемы
- Создание реального подключения к базе данных дорого. Пул соединений всегда имеет несколько дополнительных подключений и дает вам один из них.
- Если соединения терпят неудачу, пул соединений знает, как открыть новый
- Очень важно: каждый поток получает свое собственное соединение. Это означает, что процесс потоковой обработки обрабатывается там, где он должен быть: на уровне БД. БД являются суперэффективными и могут легко справляться с одновременным запросом.
- Другие вещи (например, централизованное расположение строк подключения JDBC и т.д.), но на этом есть миллионы статей, книг и т.д.
Когда нужно подключиться
Где-то в стеке вызовов, инициированном в вашем делете обслуживания (doPost, doGet, doDisco, что угодно), вы должны получить соединение, а затем вы должны поступать правильно и вернуть его в блок finally. Я должен упомянуть, что главный архитектор С# сказал, что вам нужно использовать finally
блоки на 100 раз больше, чем catch
. Истинные слова никогда не говорили...
Какой пул соединений
Вы находитесь в сервлете, поэтому вы должны использовать пул соединений, предоставляемый контейнером. Ваш код JNDI будет полностью нормальным, за исключением того, как вы получаете соединение. Насколько я знаю, все контейнеры сервлетов имеют пулы соединений.
В некоторых комментариях к вышеприведенным ответам рекомендуется использовать конкретный API пула соединений. Ваша WAR должна быть переносной и "просто развертывать". Я думаю, что это в основном неправильно. Если вы используете пул соединений, предоставляемый вашим контейнером, ваше приложение будет развертываться в контейнерах, которые охватывают несколько машин и все эти фантазии, которые предоставляет спецификация Java EE. Да, дескрипторы развертывания контейнера должны быть записаны, но что способ EE, mon.
Один комментатор упоминает, что определенные пулы подключений контейнера не работают с драйверами JDBC (он упоминает Websphere). Это звучит совершенно надуманно и смешно, так что это, вероятно, так. Когда что-то подобное произойдет, выбросьте все, что вы "должны делать" в мусор, и сделайте все возможное. Это то, за что нам платят, иногда:)
Ответ 3
Я бы использовал Commons DBCP. Это проект Apache, который управляет пулом соединений для вас.
Вы просто получите свое соединение в doGet или doPost, выполнив свой запрос, а затем закройте соединение в блоке finally. (con.close() просто возвращает его в пул, он фактически не закрывает его).
DBCP может управлять тайм-аутами подключения и восстанавливать их. То, как вы сейчас делаете, если ваша база данных опускается в течение какого-то периода времени, вам придется перезапустить приложение.
Ответ 4
Объединяете ли вы свои соединения? Если нет, вы, вероятно, должны уменьшить накладные расходы при открытии и закрытии ваших соединений.
Как только это произойдет, просто держите соединение открытым до тех пор, пока это необходимо, как предложил Джон.
Ответ 5
Лучший способ, и я сейчас просматриваю Google для лучшего справочного листа, - это использовать пулы.
При инициализации вы создаете пул, содержащий X число объектов подключения SQL к вашей базе данных. Храните эти объекты в каком-то списке, например ArrayList. Каждый из этих объектов имеет private boolean для 'isLeased', длинный для времени последнего использования и соединения. Всякий раз, когда вам нужно подключение, вы запрашиваете его у пула. Пул либо предоставит вам первое доступное соединение, проверив переменную isLeased, либо создаст новую, и добавит ее в пул. Обязательно установите метку времени. Как только вы закончите соединение, просто верните его в пул, который установит isLeased на false.
Чтобы постоянно поддерживать соединения с базой данных, вы можете создать рабочий поток, который иногда будет проходить через пул и видеть, когда последний раз использовался соединение. Если он был достаточно длинным, он может закрыть это соединение и удалить его из пула.
Преимущества использования этого метода состоят в том, что у вас нет долгого ожидания, ожидающего подключения объекта Connection к базе данных. Ваши уже установленные соединения могут быть использованы повторно так, как вам нравится. И вы сможете установить количество подключений в зависимости от того, насколько заняты вы считаете свое приложение.
Ответ 6
Вы должны поддерживать только подключение базы данных до тех пор, пока оно вам нужно, что зависит от того, что вы делаете, вероятно, входит в объем ваших методов doGet/doPost
.
Ответ 7
Пул соединений, связанный с источником данных, должен делать трюк. Вы можете получить соединение с источником данных в методе запроса сервлета (doget
/dopost
и т.д.).
dbcp, c3p0 и многие другие пулы соединений могут делать то, что вы ищете. В то время как вы объединяете соединения, вам может понадобиться объединить выражения и PreparedStatements; Кроме того, если вы являетесь средой READ HEAVY, как вы указали, вы можете захотеть кэшировать некоторые результаты, используя что-то вроде ehcache.
BR,
~ А
Ответ 8
Объедините его.
Кроме того, если вы работаете с сырым JDBC, вы можете изучить что-то, что поможет вам управлять Connection, PreparedStatement и т.д. Если у вас нет очень жестких требований "облегченности", например, поддержка поддержки Spring JDBC для упрощения вашего кода, и вы не должны использовать какую-либо другую часть Spring.
Смотрите несколько примеров здесь:
http://static.springframework.org/spring/docs/2.5.x/reference/jdbc.html
Ответ 9
Обычно вы обнаружите, что открытие соединений для каждого запроса проще в управлении. Это означает метод doPost() или doGet() вашего сервлета.
Открытие его в init() делает его доступным для всех запросов и что происходит, когда у вас есть параллельные запросы?