Мы используем 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/




Вы можете запустить get on foo, чтобы поместить его в сеанс гибернации, а затем заменить его объектом, который вы создали в другом месте. Но для того, чтобы это сработало, вы должны знать все идентификаторы ваших объектов, чтобы они выглядели правильно в Hibernate.
вызовите get (foo.id), а затем объедините (foo)
Здесь есть несколько вариантов. Во-первых, вам действительно не нужна транзакция 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), где это применимо.
Ваш режим очистки, вероятно, установлен на автоматический, что означает, что вы действительно не контролируете, когда происходит фиксация БД.
Вы также можете установить режим очистки вручную, и ваши службы / репозитории будут пытаться синхронизировать базу данных с вашим приложением только тогда, когда вы им скажете.
foo находится в сеансе, что вы имеете в виду под его заменой?