Как прервать сеанс Hibernate? - PullRequest
13 голосов
/ 02 апреля 2010

В ссылке на Hibernate несколько раз указывается, что

Все исключения, выданные Hibernate, являются фатальными. Это означает, что вы должны откатиться транзакцию базы данных и закрыть текущую Session. Вам не разрешено продолжать работа с Session, вызвавшим исключение.

Одно из наших старых приложений использует один сеанс для обновления / вставки множества записей из файлов в таблицу БД. Каждое обновление / вставка записи выполняется в отдельной транзакции, которая затем фиксируется (или откатывается в случае возникновения ошибки). Затем для следующей записи открывается новая транзакция и так далее. Но один и тот же сеанс используется на протяжении всего процесса, даже если в процессе обработки было зафиксировано HibernateException. Мы используем Oracle 9i между Hibernate 3.24.sp1 на JBoss 4.2.

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

Однако мы не нашли способа воспроизвести сбой сеанса при тестировании всего приложения (это может быть стресс-тест между прочим или ...?). Мы думали о закрытии прослушивателя БД, но мы поняли, что приложение поддерживает множество соединений, открытых к БД, и слушатель не будет влиять на эти соединения. (Это веб-приложение, активируемое раз в ночь планировщиком, но его также можно активировать через браузер.) Затем мы попытались уничтожить некоторые из этих подключений в БД во время обработки обновлений приложением, что привело к сбою обновлений, но затем приложение продолжило обновлять остальные записи. Очевидно, что Hibernate достаточно умен, чтобы снова открывать разорванные соединения под капотом, не прерывая весь сеанс.

Так что, в конце концов, это может и не быть критической проблемой, поскольку наше приложение кажется достаточно надежным даже в своем первоначальном виде. Тем не менее, проблема продолжает беспокоить меня. Я хотел бы знать:

  1. При каких обстоятельствах сеанс Hibernate действительно становится непригодным для использования после выброса HibernateException ( Обновление: и каковы симптомы)?
  2. Как воспроизвести это в тесте ( Обновление: желательно в интеграции, а не в модульном тесте)?
  3. (Какой термин подходит для такого теста?)

Ответы [ 4 ]

4 голосов
/ 02 апреля 2010

При каких обстоятельствах сеанс Hibernate действительно становится непригодным для использования после возникновения исключения Hibernate?

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

Обновление: Цитата это сообщение от разработчика 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 для каждой транзакции, сгенерированной исключительной ситуации или нет ).

Какой термин подходит для такого теста?

Интеграционный тест? Испытание на прочность?

3 голосов
/ 02 апреля 2010

Интересный. Лучший способ сказать, пойти и взглянуть на источники Hibernate. Насколько я понимаю, Session не несет много состояния вокруг, за исключением следующих вещей:

  • кэш первого уровня (см. StatefulPersistenceContext ) - он может быть фактически поврежден после сбоя транзакции;
  • очередь действий (см. ActionQueue ) - кажется, все в порядке, если вы выполняете ручную очистку;
  • прослушиватели событий - их экземпляры знают о сеансе (см. EventSource ) и, как вы могли заметить, исключения JDBC всегда вызваны сбросом ( DefaultFlushEventListener , чтобы быть более точным), но быстрый взгляд на AbstractFlushingEventListener показывает, что они действительно работают с JDBCContext и ActionQueue.

И, наконец, откат самой транзакции никак не влияет на состояние сеанса, см. JDBCTransaction .

Итак, насколько я играл, Session фактически переживает ошибочную транзакцию, за исключением того, что кэш первого уровня может быть немного неточным; чтобы обойти это, вы можете предпочесть session.refresh или session.load для явной повторной синхронизации состояний.

РЕДАКТИРОВАТЬ: самый быстрый способ проверить исключения JDBC, вероятно, session.createSQLQuery("something offensive to SQL parser").executeUpdate() - вы получите идеальный SQLGrammarException - и прерванную транзакцию =)

1 голос
/ 02 апреля 2010

Мой взгляд на это таков:

  1. Почти невозможно узнать, будет ли Hibernate работать, он столкнулся с непредвиденной ситуацией и теперь работает в неопределенном состоянии - в основном вы будете вызывать UB, выполняя больше операций над сеансом.

  2. Идея - зарегистрировать перехватчик и добавить в него HibernateException - надеюсь, что Hibernate не поймает его: D

  3. Неисправность впрыска?

0 голосов
/ 02 апреля 2010

Создать подкласс Session для тестирования. Реализуйте это, чтобы вызвать исключение в каком-то конкретном сценарии. Используйте специфичный для теста конфиг hibernate, который настраивает hibernate для использования вашей сессии тестирования.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...