режим промывки изменен в grails с АВТОМАТИЧЕСКОГО на РУЧНОЙ

#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 установит режим промывки на ручной перед проверкой, чтобы предотвратить сброс любых изменений во время проверки (это может быть довольно распространенным явлением, поскольку у вас может быть пользовательский валидатор, который запрашивает существующие данные).

См. https://github.com/grails/grails-data-mapping/blob/master/grails-datastore-gorm-hibernate4/src/main/groovy/org/codehaus/groovy/grails/orm/hibernate/validation/HibernateDomainClassValidator.java#L60

Если есть какие-либо ошибки проверки, он не вернет режим промывки обратно в АВТОМАТИЧЕСКИЙ режим. Это делается для предотвращения сохранения недопустимых объектов.

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

Комментарии:

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, и весь приведенный выше пример не имеет смысла! Мне придется просмотреть проблемный код и попытаться извлечь его, чтобы сделать проблему видимой …