Лучший способ управлять подключением к базе данных для сервлета Java

Как лучше всего управлять подключением к базе данных в сервлете Java?

В настоящее время я просто открываю соединение в функции init(), а затем закрываю его в destroy().

Однако меня беспокоит, что «постоянное» удержание соединения с базой данных может быть плохим.

Это правильный способ справиться с этим? Если нет, то какие варианты лучше?

edit: чтобы дать немного больше пояснений: я пытался просто открывать / закрывать новое соединение для каждого запроса, но при тестировании я увидел проблемы с производительностью из-за создания слишком большого количества подключений.

Есть ли смысл в совместном использовании соединения по нескольким запросам? Запросы для этого приложения почти все предназначены только для чтения и поступают довольно быстро (хотя запрашиваемые данные довольно малы).

Вы действительно должны отметить вопрос Джека Леоу как правильный. Материал J2EE (или как там его называют в 2009 году) использует сервер приложений по какой-то причине ... В любом случае, круто то, что если вы сделаете то, что говорите выше, с помощью init и destroy, я думаю, что сервлет будет последовательно складываться. запросы БД, поэтому несколько потоков будут работать медленно.

Dan Rosenstark 25.05.2009 15:34

Я изменю принятый ответ, так как правила сообщества :) Однако я бы хотел, чтобы было больше объяснений. Когда я задал этот вопрос несколько месяцев назад, я был новичком во всей сцене "java webapp", и его ответ на самом деле не объяснял, о чем он говорит.

TM. 25.05.2009 18:59

Проблема в том, что это вики сообщества, поэтому баллы не начисляются. Я тоже напишу небольшой ответ ниже.

Dan Rosenstark 25.05.2009 20:02
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
27
3
23 883
9
Перейти к ответу Данный вопрос помечен как решенный

Ответы 9

Вы должны держать соединение с базой данных открытым только до тех пор, пока оно вам нужно, что в зависимости от того, что вы делаете, вероятно, находится в пределах возможностей ваших методов doGet/doPost.

Более того, не гарантируется, что destroy () будет вызываться

Guido 28.10.2008 00:34

Приложение, над которым я работаю, имеет очень частые запросы от многих клиентов, не будет ли такой риск использовать все соединения с базой данных? (или просто создать слишком много, чтобы быть быстрым?) Просто прошу разъяснений, а не пытаюсь опровергнуть свой ответ.

TM. 28.10.2008 00:35

Конечно, вы должны использовать пул соединений, если хотите, чтобы ваше приложение масштабировалось. Таким образом, у вас будет контроль над макс. количество подключений.

Guido 28.10.2008 00:39

Вам определенно следует использовать пул соединений.

John Topley 28.10.2008 00:43

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

Как только это не мешает, просто держите соединение открытым столько, сколько необходимо, как предложил Джон.

Лучший способ, и я сейчас ищу в Google лучший справочный лист, - это использовать пулы.

При инициализации вы создаете пул, который содержит X объектов подключения SQL к вашей базе данных. Храните эти объекты в каком-нибудь списке, например в ArrayList. Каждый из этих объектов имеет частное логическое значение isLeased, длительное время последнего использования и Connection. Когда вам нужно соединение, вы запрашиваете его из пула. Пул либо предоставит вам первое доступное соединение, проверяя переменную isLeased, либо создаст новое и добавит его в пул. Обязательно установите отметку времени. Как только вы закончите с подключением, просто верните его в пул, который установит для isLeased значение false.

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

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

Обычно вы обнаружите, что открытием соединений по запросу легче управлять. Это означает, что в методе doPost () или doGet () вашего сервлета.

Открытие его в init () делает его доступным для всех запросов, а что происходит, когда у вас есть параллельные запросы?

Я бы использовал Commons DBCP. Это проект Apache, который управляет пулом соединений за вас.

Вы просто получите свое соединение в своем doGet или doPost, запустите свой запрос, а затем закроете соединение в блоке finally. (con.close () просто возвращает его в пул, но не закрывает его).

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

Пул соединений, связанный с источником данных, должен помочь. Вы можете получить соединение из источника данных в методе запроса сервлета (doget / dopost и т. д.).

dbcp, c3p0 и многие другие пулы соединений могут делать то, что вы ищете. Пока вы объединяете соединения, вы можете захотеть объединить утверждения и PreparedStatements; Кроме того, если ваша среда READ HEAVY, как вы указали, вы можете захотеть кэшировать некоторые результаты, используя что-то вроде ehcache.

BR,
~ А

Ответ принят как подходящий

Я действительно не согласен с использованием Commons DBCP. Вам действительно следует полагаться на контейнер, чтобы управлять пулом соединений за вас.

Поскольку вы используете сервлеты Java, это подразумевает работу в контейнере сервлетов, и все основные контейнеры сервлетов, с которыми я знаком, обеспечивают управление пулом соединений (спецификация Java EE может даже потребовать этого). Если ваш контейнер использует DBCP (как это делает Tomcat), отлично, в противном случае просто используйте то, что предоставляет ваш контейнер.

Абсолютно! Ради всего святого, делегируйте это контейнеру и просто реализуйте поиск JNDI для ресурса. Нет НИКАКОЙ причины заниматься этим самостоятельно.

Will Hartung 28.10.2008 02:01

Думаю, это зависит от того, как развернуто ваше приложение. Мы выполняем развертывание на многих различных серверах приложений, и работать с DBCP проще, чем изучать другой способ настройки пулов соединений для каждого сервера приложений. В любом случае все в порядке. У меня также были некоторые серверы приложений, которые не работают с дисками jdbc (Websphere)

ScArcher2 28.10.2008 04:31

Я согласен с ScArcher2. Я предпочитаю, чтобы приложение было автономным и не полагалось на внешнюю конфигурацию для запуска. В идеале нужно уметь развернуть войну и готово!

Arne Evertsson 03.02.2009 17:20

Объедините это.

Кроме того, если вы используете необработанный JDBC, вы можете изучить что-то, что поможет вам управлять Connection, PreparedStatement и т. д. Если у вас нет очень жестких требований «легковесности», использование поддержки Spring JDBC, например, упростит ваш код. lot- и вы не обязаны использовать какую-либо другую часть Spring.

См. Несколько примеров здесь:

http://static.springframework.org/spring/docs/2.5.x/reference/jdbc.html

Как все говорят, вам нужно использовать пул соединений. Почему? Что случилось? И т.п.

Что не так с вашим решением

Я знаю это, потому что когда-то думал, что это была хорошая идея. Проблема двоякая:

  1. Все потоки (запросы сервлетов обслуживаются по одному потоку на каждый) будут использовать одно и то же соединение. Таким образом, запросы будут обрабатываться по одному. Это очень медленно, даже если вы просто сидите в одном браузере и нажимаете клавишу F5. Попробуйте: этот материал звучит высокоуровнево и абстрактно, но он эмпирический и проверяемый.
  2. Если соединение разрывается по какой-либо причине, метод init не будет вызываться снова (потому что сервлет не будет выведен из эксплуатации). Не пытайтесь решить эту проблему, помещая try-catch в doGet или doPost, потому что тогда вы попадете в ад (что-то вроде написания сервера приложений без запроса).
  3. Вопреки тому, что можно было подумать, у вас не будет проблем с транзакциями, поскольку начало транзакции связано с потоком, а не только с соединением. Возможно, я ошибаюсь, но так как это все равно плохое решение, не переживайте.

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

Пулы подключений дают вам массу преимуществ, но, прежде всего, они решают проблемы

  1. Реальное подключение к базе данных стоит дорого. Пул соединений всегда имеет несколько дополнительных соединений и дает вам одно из них.
  2. В случае сбоя подключения пул подключений знает, как открыть новый.
  3. Очень важно: каждый поток получает свое собственное соединение. Это означает, что потоки обрабатываются там, где они должны быть: на уровне БД. БД очень эффективны и могут легко обрабатывать параллельные запросы.
  4. Прочие вещи (например, централизованное расположение строк подключения JDBC и т. д.), Но об этом написаны миллионы статей, книг и т. д.

Когда подключиться

Где-то в стеке вызовов, инициированном в вашем сервисном делегате (doPost, doGet, doDisco, что угодно), вы должны получить соединение, а затем вы должны сделать правильные вещи и вернуть его в блоке finally. Я должен упомянуть, что чувак, главный архитектор C#, однажды однажды сказал, что вам следует использовать блоки finally в 100 раз больше, чем блоки catch. Более правдивых слов никогда не было ...

Какой пул подключений

Вы находитесь в сервлете, поэтому вам следует использовать пул соединений, который предоставляет контейнер. Ваш код JNDI будет полностью нормальным, за исключением того, как вы получаете соединение. Насколько мне известно, все контейнеры сервлетов имеют пулы соединений.

В некоторых комментариях к ответам выше предлагается использовать вместо этого конкретный API пула соединений. Ваша WAR должна быть переносимой и «просто развертываемой». Я считаю, что это в корне неверно. Если вы используете пул соединений, предоставляемый вашим контейнером, ваше приложение будет развертываться в контейнерах, охватывающих несколько машин, и все те причудливые вещи, которые предоставляет спецификация Java EE. Да, нужно будет написать дескрипторы развертывания, специфичные для контейнера, но это метод EE, мон.

Один комментатор упоминает, что определенные пулы соединений, предоставляемые контейнером, не работают с драйверами JDBC (он / она упоминает Websphere). Это звучит совершенно неправдоподобно и нелепо, так что, вероятно, это правда. Когда случаются подобные вещи, выбросьте все, что вы «должны делать», в мусор и делайте все, что можете. Иногда нам за это платят :)

Другие вопросы по теме