Как разбить сеанс Hibernate?
В ссылке Hibernate указано несколько раз, что
Все исключения, сбрасываемые Hibernate, являются фатальными. Это означает, что вам нужно откатить транзакции базы данных и закройте текущий Session
. Вам разрешено продолжить работая с Session
, которая выбрала исключение.
В одном из наших устаревших приложений используется один сеанс для обновления/вставки многих записей из файлов в таблицу DB. Каждое обновление/вставка записи выполняется в отдельной транзакции, которая затем фиксируется (или откатывается в случае возникновения ошибки). Затем для следующей записи открывается новая транзакция и т.д. Но тот же сеанс используется в течение всего процесса, даже если HibernateException
был пойман в середине обработки. Мы используем Oracle 9i btw с Hibernate 3.24.sp1 на JBoss 4.2.
Прочитав вышеприведенное в книге, я понял, что этот проект может потерпеть неудачу. Поэтому я реорганизовал приложение для использования отдельного сеанса для каждого обновления записи. В unit test с макетным сеансом factory я могу проверить, что он теперь запрашивает новый сеанс для каждого обновления записи. Пока что так хорошо.
Однако мы не обнаружили способа воспроизвести сбой сеанса при тестировании всего приложения (это будет стресс-тест btw или...?). Мы думали о том, чтобы отключить слушателя БД, но мы поняли, что приложение поддерживает связку открытых для БД связей, и слушатель не повлияет на эти соединения. (Это веб-приложение, которое активируется каждую ночь планировщиком, но оно также может быть активировано через браузер.) Затем мы попытались убить некоторые из этих соединений в БД, пока приложение обрабатывало обновления - это привело к некоторым неудачным обновления, но затем приложение с радостью продолжало обновлять остальные записи. По-видимому, Hibernate достаточно умен, чтобы снова открыть сломанные соединения под капотом, не нарушая всю сессию.
Таким образом, это не может быть критической проблемой, поскольку наше приложение кажется достаточно надежным даже в его первоначальной форме. Тем не менее, проблема продолжает меня бить. Я хотел бы знать:
- В каких обстоятельствах сеанс Hibernate действительно становится непригодным после того, как был сброшен
HibernateException
( Обновление: и каковы симптомы)?
- Как воспроизвести это в тесте ( Обновление: желательно в интеграции, а не unit test)?
- (Какой правильный термин для такого теста?)
Ответы
Ответ 1
В каких обстоятельствах сеанс Hibernate действительно становится непригодным после исключения HibernateException?
Интересный вопрос. Я почти уверен, что видел случаи, когда сеанс все еще "работал" после такого исключения. Проблема в том, что это не гарантировано. Мне нужно копать это немного больше.Забастовкa >
Обновление: Задание это сообщение от разработчика Hibernate на форумах Hibernate:
Спящий сеанс следует отбрасывать, когда возникает какое-либо исключение. Его можно оставить в несогласованном состоянии относительно базы данных. Это не относится к сеансу только для чтения, поскольку он не использует кэширование на уровне сеанса. Кроме того, вы можете очистить кеш уровня сеанса при возникновении исключения (но мне лично не нравится этот подход, поскольку он не является чистым решением, и это может привести к будущим проблемам и проблемам).
Таким образом, проблема не в том, что если Session
является "технически применимым" или нет, проблема в том, что он может вести себя не так, как ожидалось. И вы явно этого не хотите.
Как воспроизвести это в тесте?
Вы можете сознательно нарушить условие, вызывающее бросок подкласса, например, нарушив ограничение целостности, чтобы получить ConstraintViolationException
. Как насчет чего-то подобного:
Session session = HibernateUtil.getCurrentSession();
try {
session.beginTransaction();
// execute some code that violates a unique constraint
...
session.getTransaction().commit();
} catch (HibernateException e) {
session.getTransaction().rollback();
}
// keep using the session
try {
session.beginTransaction();
...
session.getTransaction().commit();
} catch (HibernateException e) {
session.getTransaction().rollback();
}
// do we reach this point?
Но я не уверен, что это ничего не докажет. Я имею в виду, что если сеанс все еще работает, мы можем заключить только этот конкретный случай.
Обновление: Забудьте мое предложение, оно ничего не докажет. И на самом деле, я думаю, что было бы чрезвычайно сложно (технически и функционально) доказать, что все работает как ожидалось (и это все еще может быть "несчастным случаем" ) после HibernateException
.
Поэтому, вместо того, чтобы пытаться доказать что-либо, я полагаю, что правильная вещь - следовать рекомендации (или предлагаемой альтернативе, но я бы просто запросил новый Session
и close
для каждой транзакции, исключение выбрано или нет).
Какой правильный термин для такого теста?
Интеграционный тест? Тест надежности?
Ответ 2
Интересный. Лучший способ рассказать - это взглянуть на источники Hibernate. Насколько мне известно, Session
не поддерживает много состояний, за исключением следующего:
- кеш первого уровня (см. StatefulPersistenceContext) - он может быть фактически поврежден после сбоя транзакции;
- очередь действий (см. ActionQueue) - кажется, все в порядке, если вы выполняете ручную очистку;
- прослушиватели событий - их экземпляры знают о сеансе (см. EventSource) и, как вы могли заметить, исключения JDBC всегда вызваны промывкой (DefaultFlushEventListener, а точнее), но быстрый взгляд на AbstractFlushingEventListener показывает, что они фактически работают с сеансом
JDBCContext
и ActionQueue
.
И последнее, откат самой транзакции никак не влияет на состояние сеанса, см. JDBCTransaction.
Итак, насколько я играл, Session
фактически переживает неисправную транзакцию, за исключением того, что кеш первого уровня может быть немного неточным; Чтобы обойти это, вы можете выбрать session.refresh
или session.load
для явной пересинхронизации состояний.
EDIT: самый быстрый способ тестирования исключений JDBC, вероятно, session.createSQLQuery("something offensive to SQL parser").executeUpdate()
- вы получите идеальный SQLGrammarException
- и сломанную транзакцию =)
Ответ 3
Мое мнение о том, что
-
Невозможно узнать, будет ли Hibernate работать, он столкнулся с непредвиденной ситуацией и теперь работает в состоянии undefined. В основном вы будете вызывать UB, выполняя еще какие-либо операции в сеансе.
-
Идея - зарегистрируйте перехватчик и выбросите в него исключение HibernateException - надеюсь, что Hibernate не поймает его: D
-
Включение отказа?
Ответ 4
Создайте подкласс сеанса для тестирования. Внедрите его для исключения исключения в каком-то конкретном сценарии. Используйте тестовую конфигурацию hibernate, которая устанавливает спящий режим для использования сеанса тестирования.