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:
grails.gorm.autoFlush = true
in config you can force a flush:true to every save()FlushMode.AUTO
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:
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!
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With