Hibernate использует JDBС подключение к базе напрямую без использования какого-либо режима блокировки. Очень важно перед подключением Hibernate ознакомится со спецификой изоляции транзакций Вашей СУБД, потому что режим, установленный уровнем изоляции транзакций, не изменяется во время использования Hibernate. Hibernate также не блокирует объекты в памяти.
Транзакции в БД всегда обязательны, любые контакты с базой данных обязательно должны быть заключены в транзакцию.
Чтобы избежать блокировки подключения к БД, физические транзакции в базе должны быть как можно короче. Длинные транзакции делают приложение непригодным для одновременного использования с высокой нагрузкой. Не держите транзакции открытыми во время выполнения действий пользователем, вместо этого открывайте транзакцию только после окончания его работы.
Hibernate сессия работает как своего рода кеш для транзакций, который предоставляет повторные операции чтения для поиска по идентификатору или выполнения запросов, которые приводят к загрузке сущностей.
Используем сессии правильно
Не нужно открывать и закрывать сессию в отдельном потоке при каждом обращении к БД. Это же утверждение верно и для транзакций. Группируйте запросы к БД в запланированные последовательности, а их уже заворачивайте в транзакцию. По этой же причине лучше отказаться от использования автокоммита. В режиме автокоммита JDBC драйвер просто выполняет неявный вызов транзакции для каждого запроса. Например, во время чтения данных — много мелких транзакций вряд ли будут лучше, чем одна четко определенная «единица работы». «Единица работы» в нашем случае это ряд операций, которые мы хотим выполнить в БД все вместе.
Наиболее распространенный паттерн использования транзакций в клиент-серверных приложениях это паттерн session-per-request (сессия для запроса). Под запросом в данном случае имеется в виду запрос пользователя на выполнение операции, а не SQL-запрос в БД. В такой модели обработка осуществляется следующим образом:
- запрос от клиента передается на сервер, где работает Hibernate;
- открывается новая сессия;
- выполняются все операции с БД (в одной «единице работы»);
- по завершению работы, или как только ответ для клиента был подготовлен, сессия закрывается.
Используйте одну транзакцию для обслуживания клиентского запроса, запускайте и коммитьте ее при открытии и закрытии сессии соответственно. Отношения между ними получаются один-к-одному и такая модель идеально подходит для многих приложений.
Чтобы упростить эту модель Hibernate предоставляет встроенное управление «текущей сессией». Приложение может получить доступ к «текущей сессии», вызвав метод sessionFactory.getCurrentSession()
. Сессия создается только при первом обращении к методу getCurrentSession()
и закрывается во время коммита транзакции. Поэтому при использовании getCurrentSession()
не нужно беспокоиться о закрытии сессии, она закроется автоматически. Настройка результата работы метода getCurrentSession()
производится с помощью интерфейса org.hibernate.context.spi.CurrentSessionContext
и параметра конфигурации hibernate.current_session_context_class
(он должен принимать значение «thread» для автоматического управления контекстом сессии).
Hibernate Exceptions
Исключения, которые генерирует Hibernate во время работы, означают, что Вы должны сразу откатить транзакцию БД и закрыть сессию. Если ваша сессия привязана к приложению, то вы должны остановить и приложение. При этом откат транзакции БД не устанавливает объекты сущностей обратно в состояние, в котором они были до начала транзакции. Это означает, что состояние данных в БД и объектов будет не синхронизировано. Обычно это не составляет проблемы, поскольку исключения не подлежат исправлению и после отката транзакции все равно необходимо начинать все сначала.
Сессия кеширует каждый объект, который представляет из себя сущность, и если Вы держите ее открытой слишком долгое время или просто загружаете очень много данных, кеш будет расти бесконечно, пока вы не получите OutOfMemoryException
. Одним из решений является вызов методов clear()
и evict()
для управления кешем сессии.
Примеры использования
Если Hibernate не настроен на управление сессиями, то соединения с базой данных, как правило, обрабатываются простым пулом, из которого Hibernate получает соединение по мере необходимости. В этом случае работа с сессиями выглядит следующим образом:
Session sess = sessionFactory.openSession(); Transaction transaction = null; try { transaction = sess.beginTransaction(); // выполнение запросов ... transaction.commit(); } catch (RuntimeException e) { if (transaction != null) { // откат транзакции transaction.rollback(); } throw e; } finally { // закрытие сессии sess.close(); }
Гораздо более гибким решением является использование встроенного в Hibernate управления контекстом «текущей сессии»:
try { sessionFactory.getCurrentSession().beginTransaction(); // выполнение запросов sessionFactory.getCurrentSession().createQuery(...); ... sessionFactory.getCurrentSession().getTransaction().commit(); } catch (RuntimeException e) { sessionFactory.getCurrentSession().getTransaction().rollback(); throw e; }
В реальном приложении такой фрагмент кода конечно же не встретится, потому что код, который выполняет вызовы Hibernate и код, который перехватывает исключения, обычно находятся на разных уровнях.