#hibernate #grails #save #flush
#спящий режим #grails #Сохранить #промывка
Вопрос:
После обновления моего проекта Grails с 1.3.7 до 2.4.0 и после исправления различных проблем, связанных с новой версией grails, я понял, что ни одно из изменений, внесенных в какой-либо объект, больше не будет сохраняться (вообще), кроме if save(flush:true)
вызывается.
С Grails 1.3.7 поведение по умолчанию при сохранении экземпляра домена с использованием save()
заключается в том, что изменения сохраняются автоматически из-за режима гибернации FlushMode => FlushMode.AUTO
. В Grails 2.4.0 это больше не так. Режим сброса по умолчанию для сеанса гибернации внутри любого действия контроллера или класса обслуживания FlushMode.MANUAL
.
Вещи становятся еще более странными при извлечении sessionFactory.currentSession.flushMode
в BootStrap, где оно имеет значение FlushMode.AUTO
, и в действии контроллера, где оно имеет значение FlushMode.MANUAL
. Это можно проверить, создав новое приложение grails и введя println "flushMode = $sessionFactory.currentSession.flushMode"
BootStrap и действие контроллера (например, index()).
Последние 2 дня я просматривал всевозможные форумы и не нашел никакого разумного объяснения, почему это нужно было изменить в Grails 2.4.0 (или, может быть, даже в более ранних версиях). Я нашел только комментарии, в которых говорилось, что это рискованно FlushMode.MANUAL
, потому что вы можете столкнуться с устаревшими объектами при запросе базы данных после того, как некоторые другие были изменены.
Я знаю, что:
- с
grails.gorm.autoFlush = true
помощью in config вы можете принудительно сбросить: true для каждого сохранения () - в hibernate3 и hibernate4 режим промывки по умолчанию
FlushMode.AUTO
- невозможно установить режим промывки ни в Config.groovy, ни в DataSource.groovy. Я пробовал все это, и ничего не помогло:
hibernate.flush.mode = 'auto'
hibernate.flushMode = 'auto'
hibernate.session.flush.mode = 'auto'
hibernate.session.flushMode = 'auto'
dataSource.hibernate.flush.mode = 'auto'
dataSource.hibernate.flushMode = 'auto'
dataSource.hibernate.session.flush.mode = 'auto'
dataSource.hibernate.session.flushMode = 'auto'
dataSource.flush.mode = 'auto'
dataSource.flushMode = 'auto'
dataSource.session.flush.mode = 'auto'
dataSource.session.flushMode = 'auto'
Пожалуйста, может кто-нибудь пролить немного света на это?
На самом деле я хотел бы знать, является ли в Grails 2.4.0 FlushMode.MANUAL
теперь желаемым значением по умолчанию?
И если это так:
- Что с комментарием «… Предложение не в том, чтобы полностью отключить режим автоматической промывки …» Питер Ледбрук в GRAILS-7180
- Как лучше всего не сталкиваться с проблемами с устаревшими объектами, особенно при выполнении сложных манипуляций с объектами домена, где изменение, создание новых экземпляров и выполнение запросов смешаны.
Большое спасибо — Andi
Прочитав ответ Грэмса и его комментарии, я попытался лучше прояснить, с чем я борюсь, и добавил следующие упрощенные классы домена и контроллера, которые демонстрируют такое поведение:
Domain class Msg:
class Msg {
String text
static constraints = {
text nullable:true
}
}
и контроллер msg:
class MsgController {
def sessionFactory
def index = {
def out = ["*** flushMode when in controller/index =
$sessionFactory.currentSession.flushMode"]
Msg.list().each { out << "$it.id: text=$it.text" }
render out.join('<br>')
}
// this save does persist the new msg object,
// even if flushMode = MANUAL
def save1 = {
def out = ["*** flushMode when in controller/save =
$sessionFactory.currentSession.flushMode"]
def msg = new Msg(text:'hallo')
if (!msg.save()) {
out << "msg has errors! " msg.errors
}
out << "msg $msg.id created with text = $msg.text"
render out.join('<br>')
}
// this save does NOT persist the new msg object, even if its valid
// (difference is calling hasErrors()
def save2 = {
def out = ["*** flushMode when in controller/save =
$sessionFactory.currentSession.flushMode"]
def msg = new Msg(text:'hallo')
if (msg.hasErrors() amp;amp; !msg.save()) {
out << "msg has errors! " msg.errors
}
out << "msg $msg.id created with text = $msg.text"
render out.join('<br>')
}
}
Таким образом, вызов http://localhost/appname/msg/save1
вывода:
*** flushMode when in controller/save1 = MANUAL
msg 1 created with text = hallo
Здесь я не понимаю, почему в режиме гибернации сохраняется объект, даже если режим промывки является РУЧНЫМ.
И при вызове http://localhost/appname/msg/save2
вывода:
*** flushMode when in controller/save2 = MANUAL
msg null created with text = hallo
Объект не сохраняется, поскольку hibernate не выполняет сброс, поэтому никогда не вызывает команду sql «обновить …».
Но теперь кажется, что проблема не только в режиме промывки, но и в том, вызывает ли кто-то hasErrors() или нет! Я озадачен еще больше…
Если вы выполняете этот пример в Grails 1.3.7, оба действия сохранения (save1 и save2) сохраняют вновь созданный объект msg!
Ответ №1:
Grails установит режим промывки на ручной перед проверкой, чтобы предотвратить сброс любых изменений во время проверки (это может быть довольно распространенным явлением, поскольку у вас может быть пользовательский валидатор, который запрашивает существующие данные).
Если есть какие-либо ошибки проверки, он не вернет режим промывки обратно в АВТОМАТИЧЕСКИЙ режим. Это делается для предотвращения сохранения недопустимых объектов.
Вы видите, что у вас, вероятно, возникают ошибки проверки, и, хотя вы можете принудительно выполнить сброс, это нежелательно.
Комментарии:
1. Спасибо за ваше объяснение. Вот что имеет смысл. Но почему Grails переключается на РУЧНОЙ режим в действии контроллера, даже если объекты домена не сохраняются (вообще не используются)? Я написал крошечное приложение Grails всего с 1 контроллером и без класса домена, чтобы проверить это. В действие индекса контроллера я ввел только 1 LOC
println "flushMode = $sessionFactory.currentSession.flushMode"
2. Для операций чтения Grails использует транзакцию только для чтения. Транзакция, доступная только для чтения, использует режим сброса ВРУЧНУЮ. Причина, по которой операции чтения используют транзакцию только для чтения, заключается в том, что это повышает производительность, поскольку в режиме гибернации нет необходимости проверять объекты, доступные только для чтения. Это может быть то, что вы видите.
3. См. Исходный вопрос с примером обновленного домена и контроллера
4. Ваш второй пример не имеет смысла, поскольку выполнение
if (msg.hasErrors() amp;amp; !msg.save())
означает, чтоsave()
никогда не вызывается, потомуhasErrors()
что вернетfalse
5. Извините, моя опечатка! Это должно быть
if (msg.hasErrors() || !msg.save())
, и тогда оба действия сохраняют объект msg, и весь приведенный выше пример не имеет смысла! Мне придется просмотреть проблемный код и попытаться извлечь его, чтобы сделать проблему видимой …