Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

flush mode changed in grails from AUTO to MANUAL

After upgrading my Grails project from 1.3.7 to 2.4.0 and after fixing various issues related to the new grails version, I realized that none of the changes done to any object would be persisted anymore (at all) except if save(flush:true) is called.

With Grails 1.3.7 the default behavior when saving a domain instance using save() is that changes get persisted automatically, due to hibernate flushMode => FlushMode.AUTO. In Grails 2.4.0 this is not true anymore. The default flushMode of the hibernate session inside any controller action or service class is FlushMode.MANUAL.

Things get even stranger when retrieving sessionFactory.currentSession.flushMode in BootStrap, where it has a value of FlushMode.AUTO and in a controller action where it has the value FlushMode.MANUAL. This can be verified by creating a new grails app and putting println "flushMode = $sessionFactory.currentSession.flushMode" in BootStrap and in a controller action (eg. index()).

I have been searching through all kind of forums for the last 2 days and did not find any reasonable explanation why this had to be changed in Grails 2.4.0 (or maybe even in earlier versions). I only found comments saying its kind of risky to have FlushMode.MANUAL because you may run into stale objects when querying the db after some others have been modified.

I know that:

  • with grails.gorm.autoFlush = true in config you can force a flush:true to every save()
  • in hibernate3 and hibernate4 default flushMode is FlushMode.AUTO
  • its not possible to set flushMode in Config.groovy nor in DataSource.groovy. I tried all of this and nothing did the job: 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'

Please can someone throw a little light in this ?

Actually I would like to know if in Grails 2.4.0 FlushMode.MANUAL is now the desired default?

And if so:

  • What is with the comment "… The proposal is not that we disable the AUTO flush mode completely …" by Peter Ledbrook in GRAILS-7180
  • What is the best practice to not run into the problems with stale objects, especially when doing complex manipulations on domain-objects where modifying, creating new instances and querying is all mixed.

Thanks very much - Andi


Upon reading Graemes Answer and his comments, I tried to better clarify what I am struggling with and added the following simplified domain and controller classes which demonstrate that behavior:

Domain class Msg:

class Msg {

    String  text

    static constraints = {
        text nullable:true
    }
}

and the msg Controller:

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() && !msg.save()) {
            out << "msg has errors! " + msg.errors
        }
        out << "msg $msg.id created with text = $msg.text"
        render out.join('<br>')
    }
}


So calling http://localhost/appname/msg/save1 the output is:

*** flushMode when in controller/save1 = MANUAL
msg 1 created with text = hallo

Here I don't get it, why hibernate persists the object, even thou flushMode is MANUAL.

And when calling http://localhost/appname/msg/save2 the output is:

*** flushMode when in controller/save2 = MANUAL
msg null created with text = hallo

The object does not get persisted because hibernate does not issue a flush, thus never calls a sql "update ..." command.

But now it seems that not only the flushMode is an issue, but also if one calls hasErrors() or not! I am puzzled even more ...

If you do this example in Grails 1.3.7 both save actions (save1 and save2) do persist the newly created msg object!

like image 700
Andi Schweizer Avatar asked Jun 19 '14 14:06

Andi Schweizer


1 Answers

Grails will set the flush mode to Manual prior to validating to prevent any changes being flushed during validation (this can be quite common as you may have a custom validator that queries existing data).

See 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

If there are any validation errors it will not set the flush mode back to AUTO. This is to prevent invalid objects being persisted.

What you are seeing is you probably have validation errors occurring and although you can force a flush it isn't advisable.

like image 174
Graeme Rocher Avatar answered Sep 28 '22 01:09

Graeme Rocher