Подтвердить что ты не робот

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

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

//begin with all book.status 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
}
4b9b3361

Ответ 1

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

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

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

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

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

Из GORM Gotchas - часть 1

UPDATE: чтобы было ясно, как очистить сеанс один раз после того, как все объекты будут сохранены:

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()
  }
}

Ответ 2

Нужно ли мне явно скрывать вызовы GORM в grails?

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

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

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

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

если этот код имеет ошибку:

without transaction with error

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

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

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

Ответ 3

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

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

Есть два других значения для FlushMode, и если вы установите один из них, это объяснит ваши проблемы. Во-первых, это "руководство", что означает, что вы решили провести сеанс ручного сброса (например, с помощью save (flush: true)). Если вы этого не сделаете, Foo.findAllByBar читает устаревшее состояние БД. Второй - это "фиксация", что означает, что сеанс очищается при каждой транзакционной фиксации. Это очень удобно, если вы используете инструкцию "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

Ответ 4

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

gorm docs