Строка StaleObjectstateException была обновлена ​​или удалена

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

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

в основном, это второе обращение к базе данных вызывает это исключение. Это:

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

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

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

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

Я прочитал это: 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 тоже не получилось.


person Saky    schedule 01.07.2010    source источник
comment
если бы вы могли опубликовать код метода, вызывающего исключение, я рассмотрю его дальше. Звучит как-то подозрительно   -  person walnutmon    schedule 01.07.2010


Ответы (4)


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

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

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

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

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

person Thomas W    schedule 15.11.2011

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

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

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

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

person barclay    schedule 15.09.2011

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

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

Добавьте поддержку оптимистической блокировки с помощью столбца TimeStamp или Version.

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

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

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

person Puspendu Banerjee    schedule 05.11.2010
comment
Я думаю, что предложение использовать инверсию, вероятно, сбивает с толку в контексте распространения изменений от родителя к дочернему. Для этого фактическим поведением для настройки является каскад. Обратное как понятие ортогонально каскадному - person Shailendra; 27.09.2013

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

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

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

person walnutmon    schedule 01.07.2010
comment
Спасибо за предложение. Я только что проверил, что в функции getEquipmentsByNumber после чтения переменных из списка свойств я делаю следующее: getHibernateTemplate().flush(); и теперь исключение находится в этой строке, а не в вызове извлечения материала (то есть getMaterialById(id)) Я не использую аннотации, так как не очень хорошо с ними знаком. - person Saky; 01.07.2010