Проблема с транзакцией гибернации

Мы используем Hibernate Spring MVC с фильтром OpenSessionInView. Вот проблема, с которой мы сталкиваемся (псевдокод)

transaction 1 
load object foo
transaction 1 end

update foo's properties (not calling session.save or session.update but only foo's setters)

validate foo (using hibernate validator)
if validation fails ?
 go back to edit screen
 transaction 2 (read only)
 load form backing objects from db
 transaction 2 end
 go to view
else 
transaction 3 
session.update(foo)
transaction 3 end

у нас есть проблема, если проверка не удалась foo помечается как "грязный" в сеансе гибернации (поскольку мы используем OpenSessionInView, у нас есть только один сеанс на протяжении всего http-запроса), когда мы загружаем объекты поддержки формы (например, список некоторых сущностей, использующих запрос HQL), переходим в спящий режим перед выполнением query проверяет, есть ли в сеансе грязные объекты, он видит, что foo есть, и сбрасывает его, когда транзакция 2 фиксируется, обновления записываются в базу данных. Проблема в том, что даже если это транзакция только для чтения и даже если foo не обновлялся в транзакции 2, hibernate не знает, какой объект был обновлен в какой транзакции, и не сбрасывает только объекты из этой транзакции. Какие-либо предложения? сталкивался ли кто-нибудь раньше с подобной проблемой?

Обновление: этот пост проливает свет на проблему: http://brian.pontarelli.com/2007/04/03/hibernate-pitfalls-part-2/

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
1
0
2 750
6

Ответы 6

Вы можете запустить get on foo, чтобы поместить его в сеанс гибернации, а затем заменить его объектом, который вы создали в другом месте. Но для того, чтобы это сработало, вы должны знать все идентификаторы ваших объектов, чтобы они выглядели правильно в Hibernate.

foo находится в сеансе, что вы имеете в виду под его заменой?

talg 29.10.2008 00:28

вызовите get (foo.id), а затем объедините (foo)

Elie 29.10.2008 16:38

Здесь есть несколько вариантов. Во-первых, вам действительно не нужна транзакция 2, поскольку сеанс открыт, вы можете просто загрузить резервные объекты из базы данных, тем самым избегая грязной проверки в сеансе. Другой вариант - удалить foo из сеанса после того, как он будет получен, а затем использовать session.merge (), чтобы повторно присоединить его, когда вы, какие изменения должны быть сохранены.

В спящем режиме важно понимать, что именно творится под покровом. На каждой границе фиксации он будет пытаться сбросить все изменения объектов в текущем сеансе, независимо от того, были ли внесены изменения в текущую транзакцию или в любую транзакцию вообще в этом отношении. Таким образом, вам действительно не нужно вызывать session.update () для любого объекта, который уже находится в сеансе.

Надеюсь это поможет

А как насчет использования Session.clear () и / или Session.evict ()?

Как насчет установки singleSession = false в фильтре? Это может поместить ваши операции в отдельные сеансы, поэтому вам не придется иметь дело с проблемами кеша 1-го уровня. В противном случае вы, вероятно, захотите отсоединить / прикрепить свои объекты вручную, как предлагает пользователь выше. Вы также можете изменить FlushMode в своем сеансе, если не хотите, чтобы все данные сбрасывались автоматически (FlushMode.MANUAL).

Здесь есть проблема дизайна. Как вы думаете, ORM - это прозрачная абстракция вашего хранилища данных, или вы думаете, что это набор библиотек для управления данными? Я бы сказал, что Hibernate - это первое. Вся причина его существования состоит в том, чтобы удалить различие между состоянием вашего объекта в памяти и состоянием вашей базы данных. Он действительно предоставляет низкоуровневые механизмы, позволяющие отделить их друг от друга и работать с ними по отдельности, но тем самым вы удаляете большую часть значения Hibernate.

Все очень просто - Hibernate = ваша база данных. Если вы не хотите, чтобы что-то сохранялось, не меняйте постоянные объекты.

Проверяйте свои данные перед обновлением объектов домена. Обязательно проверяйте и объекты домена, но это последняя линия защиты. Если вы получили ошибку проверки для постоянного объекта, не проглатывайте исключение. Если вы не предотвратите это, Hibernate сделает все правильно, а именно тут же закроет сеанс.

Реализуйте уровень обслуживания, взгляните на аннотацию Spring @Transactional и отметьте свои методы как @Transactional (readOnly = true), где это применимо.

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

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

Другие вопросы по теме