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

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

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

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

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

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

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

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

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

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

UPDATE: То, что я только что проверил, это то, что в функции getEquipmentsByNumber() после чтения переменных из списка свойств я делаю это: getHibernateTemplate().flush();, и теперь исключение находится в этой строке, а не в вызове для извлечения материала (т.е. getMaterialById(id)).

UPDATE: Перед явным вызовом flush я удаляю объект из кеша сеанса, чтобы в кэше не осталось ничего устаревшего объекта.

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

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

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

Ответы

Ответ 1

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

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

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

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

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

Ответ 2

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

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

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

Теперь странно, что в нашей производственной среде с предположительно проблематичным отображением мы не получаем этого исключения. Кто-нибудь знает, почему это может быть? Мы используем ту же версию MySQL - "5.0.22-log" (я не знаю, что означает "-log" ) - в dev и production envs.

Ответ 3

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

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

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

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

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

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

Ответ 4

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

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

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