Строка StaleObjectstateException была обновлена ​​или удалена - PullRequest
15 голосов
/ 01 июля 2010

Я получаю это исключение в контроллере веб-приложения на основе Spring Framework, использующего Hibernate.Я пробовал много способов противостоять этому, но не смог разрешить его.

В методе контроллера, handleRequestInternal, в базу данных поступают вызовы, в основном для «чтения», если только это не действие отправки.Я использовал Spring Session, но перешел на getHibernateTemplate(), и проблема все еще остается.

По сути, этот второй вызов базы данных вызывает это исключение.То есть:

1) getEquipmentsByNumber(number) {сначала оборудование выбирается из БД на основе «числа», у которого есть список свойств, а у каждого свойства есть список значений.Я перебираю эти значения (строки примитивных объектов) для чтения переменных)

2) getMaterialById(id) {выбирает материалы на основе идентификатора}

Я понимаю, что второй вызов, скорее всего,, делает сеанс "сбрасывать", но я только "читаю" объекты, тогда почему второй вызов вызывает исключение состояния устаревшего объекта в свойстве Equipment, если ничего не изменилось?

Я не могу очистить кеш после вызова, так как это вызывает исключения LazyException для объектов, которые я передаю в представление.

Я прочитал это: https://forums.hibernate.org/viewtopic.php?f=1&t=996355&start=0, но не смог решить проблемуна основе предоставленных предложений.

Как я могу решить эту проблему?Любые идеи и мысли приветствуются.

ОБНОВЛЕНИЕ: Я только что проверил, что в функции getEquipmentsByNumber() после чтения переменных из списка свойств я делаю это: getHibernateTemplate().flush(); итеперь исключение находится в этой строке, а не в вызове для выборки материала (то есть getMaterialById(id)).

ОБНОВЛЕНИЕ: Перед явным вызовом сброса я удаляю объект из кэша сеанса, поэтомучто в кэше не осталось устаревших объектов.

getHibernateTemplate().evict(equipment);
getHibernateTemplate().flush();

ОК, так что теперь проблема перешла к следующей выборке из БД после того, как я это сделал.Я полагаю, что я должен пометить методы как синхронизированные и исключить объекты, как только я закончу читать их содержимое!звучит не очень хорошо.

ОБНОВЛЕНИЕ: Сделан метод handleRequestInternal "синхронизирован".Ошибка исчезла.Конечно, не лучшее решение, но что делать!Попытался в handleRequestInternal закрыть текущий сеанс и открыть новый.Но это может привести к тому, что другие части приложения не будут работать должным образом.Пытался использовать ThreadLocal, который тоже не работал.

Ответы [ 4 ]

6 голосов
/ 15 ноября 2011

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

Вот почему вызов flush() вызывает исключение.

Одна возможность: вы неправильно «делитесь» сессией или сущностями, через поля (поля) вашего сервлета или контроллера . Это главная причина, по которой «синхронизированный» изменил бы ваши симптомы ошибки. Краткое решение: никогда не делайте этого. Сессии и сущности не должны и не должны работать таким образом - каждый Запрос должен обрабатываться независимо.

Другая возможность: unsaved-value по умолчанию 0 для полей типа "int" . Вместо этого вы можете ввести их как «Integer», если вы действительно хотите использовать 0 в качестве действительного значения PK.

Третье предложение: явно использовать Hibernate Session, научиться писать простой правильный код, который работает , затем загрузить исходный код Java для библиотек Hibernate / Spring, чтобы вы могли прочитать и понять, что эти библиотеки на самом деле делают для вас .

3 голосов
/ 15 сентября 2011

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

org.hibernate.StaleObjectStateException: строка была обновлена ​​или удалена другая транзакция (или сопоставление несохраненного значения было неверным): [Com.rc.model.mexp.MerchantAccount # 59132]

В нашем случае оказалось, что отображение было неправильным; у нас было type="text" в отображении для одного поля, которое было средним типом в базе данных, и кажется, что Hibernate действительно ненавидит это, по крайней мере, при определенных обстоятельствах. Мы полностью удалили спецификацию типа из сопоставления для этого поля, и проблема была решена.

Теперь странная вещь заключается в том, что в нашей производственной среде с предположительно проблемным отображением мы НЕ получаем этого исключения. Кто-нибудь знает, почему это может быть? Мы используем одну и ту же версию MySQL - «5.0.22-log» (я не знаю, что означает «-log») - в средах разработки и разработки.

2 голосов
/ 05 ноября 2010

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

Используйте двунаправленное отображение с inverse=true между родительским объектом и дочерним объектом, чтобы изменение в родительском или дочернем объекте правильно распространялось на другой конец отношения.

Добавить поддержку Оптимистическая блокировка , используя TimeStamp или Version столбец

Использовать запрос на объединение , чтобы получить весь объектный граф [parent + children] вместечтобы вообще избежать второго звонка.

Наконец, если и только если ничего не работает: Загрузите родителя снова на Id (у вас это уже есть) и заполните измененные данные, затем обновите.

Жизнь будетбудь хорошим!:)

0 голосов
/ 01 июля 2010

Эта проблема была тем, с чем я столкнулся, и меня это очень расстраивало, хотя в ваших вызовах DAO / Hibernate должно происходить что-то странное, потому что, если вы выполняете поиск по идентификатору, нет причин получатьустаревшее состояние, поскольку это просто поиск объекта.

Во-первых, убедитесь, что все ваши методы помечены @Transaction(required=true) // you'll have to look up the exact syntax

Однако это исключение обычно выдается при попыткевнести изменения в объект, который был отсоединен от сеанса, из которого он был извлечен.Решение этой проблемы часто не простое и требует большего количества размещенного кода, чтобы мы могли точно видеть, что происходит;Мое общее предложение было бы создать @Service, который выполняет подобные вещи в одной транзакции

...