Как Hibernate решает порядок обновления / вставки/удаления

давайте сначала забудем о спящем режиме. Предположим, что у меня есть две таблицы, A & B. две транзакции обновляют одни и те же записи в этих двух таблицах, но TXN 1 обновляет B, а затем A, в то время как TXN 2 обновляет a, то B. Это типичный пример взаимоблокировки. Наиболее распространенным способом избежать этого является предварительное определение порядка приобретения ресурсов. Например, мы должны обновить таблицу A, а затем B.

вернуться в спящий режим. Когда мы обновляем множество объектов за один сеанс, как только я смываю сеанс, изменения различных сущностей будет генерировать соответствующие инструкции insert/update / delete в БД. Есть ли у Hibernate какой-то алгоритм для определения порядка обновления среди объектов? Если нет, то как Hibernate используется для предотвращения тупиковой ситуации, описанной в 1-м абзаце?

Если Hibernate поддерживает порядок, как я могу знать или контролировать порядок? Я не хочу, чтобы мое явное обновление в DB конфликтовало с Hibernate и вызывало взаимоблокировку.

2 ответов


проблема, которую вы описываете, не обрабатывается базой данных, и, по моему опыту, не полностью обрабатывается Hibernate.

вы должны предпринять явные шаги, чтобы избежать проблемы.

Hibernate делает некоторую работу за вас. В соответствии с предыдущим ответом Hibernate гарантирует, что в изолированном потоке вставки, удаления и обновления упорядочены таким образом, что они будут применяться в достижимом порядке. Видеть performExecutions (сеанс источника событий) в классе AbstractFlushingEventListener:

выполните все SQL (и обновления кэша второго уровня) в специальном порядке, чтобы ограничения внешнего ключа не могли быть нарушены:

  1. вставки, в порядке их выполнения
  2. обновления
  3. удаление элементов коллекции
  4. вставка элементов коллекции
  5. удаляет, в порядке они были исполнены

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

обратите внимание, что он не определяет порядок обновления. Изучение кода Hibernate приводит меня к мысли, что порядок обновления будет зависеть от порядка, в котором сущности были добавлены в контекст сохранения,не порядок их обновления. Это может быть предсказуемо в вашем коде, но чтение кода Hibernate не оставило меня чувство, что я буду полагаться на этот заказ.

есть три решения, которые я могу думать из:

  1. попробуйте спящий режим.order_updates на правда. Это должно помочь избежать взаимоблокировок при обновлении нескольких строк в одной таблице, но не поможет с взаимоблокировками в нескольких таблицах.
  2. Сделайте ваши транзакции принять PESSIMISTIC_WRITE блокировка на одном из объектов перед выполнением каких-либо обновлений. Какой объект вы используете, будет зависеть от вашей конкретной ситуации, но пока вы гарантируете, что объект выбран последовательно, если существует риск взаимоблокировки, это заблокирует остальную часть транзакции до тех пор, пока блокировка не будет получена.
  3. напишите свой код, чтобы поймать тупики, когда они происходят, и повторите попытку разумным образом. Компонент, управляющий повторной попыткой блокировки, должен находиться за пределами текущей границы транзакции. Это связано с тем, что неудачный сеанс должен быть закрыт, а связанная транзакция должна иметь поддержку отката. В в этой статье вы можете найти пример автоматическая повторная попытка аспекта AOP.

Что касается первого примера - такие вещи обрабатываются базой данных (подробнее об уровнях изоляции транзакций и стратегиях блокировки вашей базы данных). Есть много разных способов справиться с этим.

Что касается спящего режима, javadoc для org.зимовать.событие.защита.AbstractFlushingEventListener.performExecutions (EventSource) говорит:

выполните все обновления кэша SQL и второго уровня в специальном порядке, чтобы ограничения внешнего ключа не могли быть нарушено:

  1. вставки, в порядке их выполнения
  2. обновление удаление элементов коллекции
  3. вставка элементов коллекции
  4. удаляет, в порядке их выполнения

Я предполагаю, что это единственная оптимизация выполненных SQL-запросов Hibernate. Остальные проблемы решаются с помощью базы данных.