Мне нравится ссылаться на эту проблему как на проблему "повторяемого искателя", потому что она в некотором смысле противоположна "не повторяемому чтению". Поскольку hibernate повторно использует объекты, прикрепленные к его сеансу, результаты поиска могут включать некоторые старые версии объектов, которые теперь устарели.
Проблема заключается в технически разработке Hibernate, но поскольку сеанс Hibernate неявна в доменах Grails и Grails, долговечны (HTTP-запрос длинный для меня). Я решил задать этот вопрос в контексте Grails/GORM.
Я хотел бы спросить экспертов здесь, если есть общепринятые стратегии для решения этой проблемы.
Рассмотрим это:
class BankAccount {
String name
Float amount
static constraints = {
name unique: true
}
}
и 'componentA':
BankAccount.findByName('a1')
'код компонента:
def result = BankAccount.findAll()
Предположим, что компонент A выполняется первым, за которым следует другая логика, за которой следует компонентB, результат из компонента B визуализируется представлением. Компоненты A и B не хотят много знать друг о друге.
Таким образом, компонент resultB содержит старую версию BankAccount 'a1'.
Многие очень неловкие вещи могут произойти. Если BankAccounts были одновременно изменены, представленный список может содержать, например, 2 элемента с именем "a1" (уникальность выглядит у пользователя!), Или денежные переводы между счетами могут отображаться как частично примененная транзакция (если деньги были переведены с a2 на a1, то он будет вычитаться из a2, но еще нет для a1). Эти проблемы смущают и могут снизить доверие пользователей к приложению.
( ADDED 9/24/2014: Вот пример открытия глаз, это утверждение может завершиться неудачно:
BankAccount.findAllByName('a1').every{ it.name == 'a1' }
Примеры того, как это происходит, можно найти в любом из связанных билетов JIRA или в моем блоге. )
( ДОБАВЛЕНО 9/24/2014: ПРИМЕЧАНИЕ. По-видимому, разумный совет по использованию уникальных ключей с использованием базы данных при реализации метода equals() не безопасен concurrency. Вы можете получить 2 объекта с помощью то же значение "бизнес-ключа", которое отличается.)
Возможные решения, похоже, добавляют много вызовов discard() или много вызововNewSession() и обрабатывают LazyIntializationExeption и DuplicateKeyException и т.д.
Но если я это сделаю, то почему я использую hibernate/GORM? Вызов обновления для каждого объекта, возвращаемого из каждого запроса, кажется просто смешным.
Мое нынешнее мышление заключается в том, что использование коротких сеансов /withNewSession в некоторых критических областях - лучший подход, но во всех случаях это не решает проблему, а только некоторые критические области приложений.
С чем это связано с приложениями Grails? Можете ли вы указать мне на какую-либо документацию/обсуждение этой проблемы?
EDITED 9/24/2014: Релевантный билет Grails JIRA: https://jira.grails.org/browse/GRAILS-11645, Hibernate JIRA: https://hibernate.atlassian.net/browse/HHH-9367 (к сожалению, было отклонено), мой блог содержит более подробные примеры: http://rpeszek.blogspot.com/2014/08/i-dont-like-hibernategrails-part-2.html
ДОБАВЛЕНО 10/17/2014: У меня появилось несколько ответов, в которых говорится, что это любое приложение DB/любая проблема ORM. Это неверно.
Верно, что этой проблемы можно избежать, используя длинные транзакции (длина сеанса Hibernate/длина HTTP-запроса) + установка выше, чем нормальный уровень изоляции БД REPEATABLE READ. Это решение просто неприемлемо (почему у нас есть транснациональные сервисы, если для работы приложения должным образом нужны HTTP-запросы длинных транзакций??)
Приложения БД и другие ОРМ не будут демонстрировать эту проблему. Им не понадобятся длительные транзакции, и проблема предотвращается только с помощью READ COMMITTED.
Сейчас уже 2 месяца, так как я разместил этот вопрос здесь, и он не получил значимого ответа. Это просто потому, что у этой проблемы нет ответа. Это то, что Hibernate может исправить, а не то, что приложение Grails может исправить. ADDED 10/17/2014-END