Строка StaleObjectstateException была обновлена или удалена
Я получаю это исключение в контроллере веб-приложения на основе Spring Framework с использованием Hibernate. Я пробовал много способов противостоять этому, но не смог решить.
В методе контроллера, handleRequestInternal
в базу данных поступают вызовы, в основном для "чтения", если только это не действие "отправить". Я использовал Spring Session, но перешел на getHibernateTemplate()
и проблема все еще остается.
в основном, этот второй вызов базы данных вызывает это исключение. То есть:
1) getEquipmentsByNumber(number)
{сначала оборудование выбирается из БД на основе "числа", которое имеет список свойств, а каждое свойство имеет список значений. Я перебираю эти значения (примитивные объекты Strings) для чтения переменных)
2) getMaterialById(id)
{выбирает материалы на основе идентификатора}
Я понимаю, что второй вызов, скорее всего, вызывает "сброс" сеанса, но я только "читаю" объекты, тогда почему второй вызов вызывает исключение состояния устаревшего объекта в свойстве Equipment, если ничего не изменилось?
Я не могу очистить кеш после вызова, так как это вызывает исключения LazyException для объектов, которые я передаю представлению.
Я прочитал это: https://forums.hibernate.org/viewtopic.php?f=1&t=996355&start=0 но не смог решить проблему на основе предоставленных предложений.
Как я могу решить эту проблему? Любые идеи и мысли приветствуются.
ОБНОВЛЕНИЕ: что я только что проверил, что в функции getEquipmentsByNumber()
после прочтения переменных из списка свойств, я делаю это: getHibernateTemplate().flush();
и теперь исключение находится в этой строке, а не вызове выборки материала (то есть getMaterialById(id)
).
ОБНОВЛЕНИЕ: Прежде чем явно вызвать flush, я удаляю объект из кэша сеанса, чтобы в кэше не осталось устаревших объектов.
getHibernateTemplate().evict(equipment);
getHibernateTemplate().flush();
Хорошо, теперь проблема перешла к следующему извлечению из БД после того, как я это сделал. Я полагаю, что мне нужно пометить методы как синхронизированные и удалить объекты, как только я закончу читать их содержимое! это звучит не очень хорошо.
ОБНОВЛЕНИЕ: Сделано handleRequestInternal
метод "синхронизирован". Ошибка исчезла. Конечно, не лучшее решение, но что делать! Пробовал в handleRequestInternal
закрыть текущий сеанс и открыть новый. Но это приведет к тому, что другие части приложения не будут работать должным образом. Пытался использовать ThreadLocal
это тоже не сработало.
4 ответа
Вы неправильно используете Hibernate, что заставляет его думать, что вы обновляете или удаляете объекты из базы данных.
Вот почему звоню flush()
бросает исключение.
Одна из возможностей: вы неправильно "делитесь" сессией или сущностями через поля (ы) вашего сервлета или контроллера. Это основная причина, по которой "синхронизированный" изменит ваши симптомы ошибки. Краткое решение: никогда не делайте этого. Сессии и сущности не должны и не должны работать таким образом - каждый запрос должен обрабатываться независимо.
Другая возможность: unsaved-value
по умолчанию 0 для полей типа "int". Вместо этого вы можете ввести их как "Integer", если вы действительно хотите использовать 0 в качестве действительного значения PK.
Третье предложение: используйте Hibernate Session явно, научитесь писать простой правильный код, который работает, а затем загрузите исходный код Java для библиотек Hibernate/ Spring, чтобы вы могли прочитать и понять, что эти библиотеки на самом деле делают для вас.
Я также боролся с этим исключением, но когда оно продолжало повторяться, даже когда я устанавливал блокировку на объект (и в тестовой среде, где я знал, что я был единственным процессом, касающимся объекта), я решил указать в скобках в стеке проследите его должное рассмотрение.
org.hibernate.StaleObjectStateException: строка была обновлена или удалена другой транзакцией (или отображение несохраненного значения было неправильным): [com.rc.model.mexp.MerchantAccount#59132]
В нашем случае оказалось, что отображение было неправильным; мы имеем type="text"
в отображении для одного поля, которое было средним типом в базе данных, и кажется, что Hibernate действительно ненавидит это, по крайней мере, при определенных обстоятельствах. Мы полностью удалили спецификацию типа из сопоставления для этого поля, и проблема была решена.
Теперь странно то, что в нашей производственной среде с предположительно проблемным отображением мы НЕ получаем это исключение. Кто-нибудь знает, почему это может быть? Мы используем одну и ту же версию MySQL - "5.0.22-log" (я не знаю, что означает "-log") - в средах разработки и разработки.
Вот 3 варианта (поскольку я точно не знаю, какой вид обработки Hibernate-сессии вы используете). Добавить один за другим и проверить:
Используйте двунаправленное отображение с inverse=true
между родительским объектом и дочерним объектом, поэтому изменение в родительском или дочернем объекте будет правильно распространяться на другой конец отношения.
Добавить поддержку для Оптимистичной блокировки, используя TimeStamp
или же Version
колонка
Используйте запрос соединения, чтобы получить весь граф объектов [parent + children] вместе, чтобы вообще избежать второго вызова.
Наконец, если и только если ничего не работает: снова загрузите родительский Id
(у вас это уже есть) и заполните измененные данные, затем обновите.
Жизнь будет хорошей!:)
Эта проблема была чем-то, что я испытал, и меня это очень расстраивало, хотя должно быть что-то странное в ваших вызовах DAO/Hibernate, потому что если вы выполняете поиск по идентификатору, нет причин получать устаревшее состояние, так как это просто поиск объекта.
Во-первых, убедитесь, что все ваши методы помечены @Transaction(required=true) // you'll have to look up the exact syntax
Однако это исключение обычно выдается при попытке внести изменения в объект, который был отсоединен от сеанса, из которого он был получен. Решение этой проблемы часто не простое и требует большего количества размещенного кода, чтобы мы могли точно видеть, что происходит; мое общее предложение было бы создать @Service
который выполняет такие вещи в одной транзакции