问题描述
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!
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).
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.
这篇关于刷新模式在 grails 中从 AUTO 更改为 MANUAL的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!