Строка 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
, который выполняет такие вещи в рамках одной транзакции