Нужно ли мне когда-нибудь явно сбрасывать GORM, сохраняя вызовы в Grails? - PullRequest
35 голосов
/ 09 июня 2011

У меня странная ситуация, которая указывает на проблему с кэшированием в GORM

//begin with all book.status's as UNREAD
Book.list().each { book.status = Status.READ ; book.save() }

println (Book.findAllByStatus (Status.READ)) //will print an empty list
println (Book.list().findAll (it.status == Status.READ)) // will print all books   

Я не могу понять, почему два последних запроса могут возвращать разные результаты.

Однако, если я сделаю следующеемодификация book.save (flush: true) .Оба оператора println вернут все книги.

У меня сложилось впечатление, что в одном приложении это не нужно.

Для справки я использую

  • DB: mysql
  • Groovy: 1.7.10
  • Grails: 1.3.7

@ Hoàng Long

Моя проблема показана ниже, предположим, что action1 / action2 оба вызываются много-много раз, без какого-либо конкретного паттерна

def action1 = {
   Foo foo = Foo.get(params.id)
   //... modify foo 
   foo.save() //if I flush here, it will be inefficient if action1 is called in sequence
}

def action2 = {
   //if I flush here, it will be inefficient if action2 is called in sequence
   List<Foo> foos = Foo.findAllByBar (params.bar)
   //... do something with foos
}

Одним из решений было бы иметь флаг, который устанавливается action1 и используется action2 дляпри необходимости промойте.Моя проблема в том, что это слишком сложное решение, которое не масштабируется, так как увеличивается сложность вызовов БД.

boolean isFlushed = true

def action1 = {
   Foo foo = Foo.get(params.id)
   //... modify foo 
   foo.save() 
   isFlushed = false
}

def action2 = {
   if (!isFlushed) {
      //flush hibernate session here
   }
   List<Foo> foos = Foo.findAllByBar (params.bar)
   //... do something with foos
}

Ответы [ 4 ]

49 голосов
/ 03 февраля 2015

Нужно ли когда-либо явно сбрасывать вызовы GORM для сохранения в граалях?

Короче говоря Да !, если вы хотите использовать объект немедленно, как в своем коде.

Я столкнулся с той же проблемой, так что это картинка, которую я получил после прочтения некоторых ссылок.

Это сеанс гибернации выпуск .
Сеанс гибернации создается, когда вызывается действие контроллера, и заканчивается, когда действие возвращается (или умирает с ошибкой раньше). Если код не вызывает никакого транзакционного кода, взаимодействие с базой данных Hibernate можно изобразить так:
Предположим, что имя действия записи actionName , и вызов действия завершается без ошибок.

NB : Средняя полоса (кэш 2-го уровня отключен), так как код транзакции отсутствует. without transaction without error

, если в приведенном выше коде есть ошибка:

without transaction with error

Но если ваше действие вызывает транзакционный метод или создает встроенную транзакцию с withTransaction (и примите вызов к действию, выполненному без какой-либо ошибки). with transaction without error

Если в приведенном выше коде есть ошибка: with transaction with error

Надеюсь, это поможет, но если я допустил какую-либо ошибку или пропустил крупный комментарий, прокомментируйте меня, я обновлю свои фото.

31 голосов
/ 09 июня 2011

В вашем случае первый оператор возвращает пустой список, потому что он читает данные из базы данных, но данных еще нет.

Вот как работает Hibernate: когда вы вызываете save с (flush: true), он сбрасывает сеанс Hibernate, сохраняя все данные в сеансе в базе данных немедленно . Если не использовать (flush:true), данные записываются только в сеансе Hibernate и сохраняются в базе данных только при сбрасывании сеанса Hibernate . Время очистки сеанса автоматически определяется Hibernate для оптимизации производительности.

Как правило, вы должны позволить Hibernate делать всю работу за вас (для оптимизации) - если только вы не хотите, чтобы данные сохранялись сразу.

По словам Питера Ледбрука:

Пусть Hibernate выполнит свою работу и сбросит сессию только вручную, когда вы должен, или, по крайней мере, только в конце партии обновлений. Вы должны только действительно использовать, если вы не видите данные в базе данных, когда это должно быть там. Я знаю это немного слабоватый, но обстоятельства когда такие действия необходимы, зависит на реализацию базы данных и другие факторы.

С GORM Gotchas - часть 1

ОБНОВЛЕНИЕ: чтобы понять, как очистить сессию один раз после сохранения всего объекта:

import org.hibernate.*

class SomeController {
  SessionFactory sessionFactory

  def save = {
    assert sessionFactory != null

    // loop and save your books here

    def hibSession = sessionFactory.getCurrentSession()
    assert hibSession != null
    hibSession.flush()
  }
}
8 голосов
/ 05 августа 2013

Интересно, какой была ваша настройка FlushMode.

По умолчанию установлено значение " auto ", и это означает, что сеанс сбрасывается перед каждым запросом, который напрямую попадает в БД (и, вероятно, в других случаях). В этом случае ваш Foo.findAllByBar должен сначала очистить сеанс (возможная проблема с производительностью!) И прочитать правильное значение из БД.

Есть два других значения для FlushMode, и если вы установите одно из них, это объяснит ваши проблемы. Во-первых, это « manual », что означает, что вы решили выполнить сеанс ручной очистки (например, с помощью save (flush: true)). Если вы этого не сделаете, тогда Foo.findAllByBar считывает устаревшее состояние БД. Второй - « commit », что означает, что сессия сбрасывается при каждой транзакции. Это очень удобно, если вы используете оператор " withTransaction " в Grails.

Ресурсы: http://schneide.wordpress.com/2011/03/08/the-grails-performance-switch-flush-modecommit/ http://docs.jboss.org/hibernate/entitymanager/3.5/reference/en/html/objectstate.html#d0e1215

1 голос
/ 14 мая 2015

Для добавленной информации вы не можете использовать flush или save (flush: true) в событиях класса вашего домена (afterUpdate, beforeUpdate, ect). Это приведет к ошибке переполнения стека. Вы можете использовать save () без очистки.

Горм документы

...