Подтвердить что ты не робот

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

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

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

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

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

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

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

4b9b3361

Ответ 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() делает его доступным для всех запросов и что происходит, когда у вас есть параллельные запросы?