Search code examples
grailsgrails-ormobject-persistence

Do I ever need to explicitly flush GORM save calls in grails?


I have a strange situation which appears to indicate a GORM cacheing problem

//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   

I cannot understand why the last two queries could return different results.

However if I make the following modification of book.save(flush:true). Both of the println statements will return all books.

I was under the impression that this was not necessary within a single application.

For reference I'm using

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

@Hoàng Long

My problem is demonstrated below, suppose action1/action2 are both called many many times, in no particular pattern

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
}

One solution would be to have a flag which is is set by action1 and used by action2 to flush if necessary. My issue is that this is an overly complex solution, which is not scalable as the complexity of DB calls increases.

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
}

Solution

  • In your case, the first statement return empty list because it reads data from the database, but the data isn't there yet.

    It's how Hibernate works: When you call save with (flush: true), it will flush the Hibernate session, persistent all data in session to database immediately. If not using (flush:true), the data is only recorded in Hibernate session and only get persisted in database when Hibernate session is flushed. The time to flush the session is automatically determined by Hibernate to optimize the performance.

    Generally, you should let Hibernate do the work for you (for optimization sake) - unless you want the data are persisted right away.

    According to Peter Ledbrook:

    Let Hibernate do it's job and only manually flush the session when you have to, or at least only at the end of a batch of updates. You should only really use if you're not seeing the data in the database when it should be there. I know that's a bit wishy-washy, but the circumstances when such action is necessary depend on the database implementation and other factors.

    From GORM Gotchas - part 1

    UPDATE: to be clear about how to flush the session one time after all the object get saved:

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