Nach dem Upgrade meines Grails-Projekts von 1.3.7 auf 2.4.0 und nach der Behebung verschiedener Probleme im Zusammenhang mit der neuen Grails-Version erkannte ich, dass keine der Änderungen an einem Objekt vorgenommen wurden persistent (überhaupt), außer wenn save(flush:true)
aufgerufen wird.Flush-Modus in Grails von AUTO zu MANUAL geändert
Mit Grails 1.3.7 ist das Standardverhalten beim Speichern einer Domäneninstanz mit save()
, dass Änderungen automatisch beibehalten werden, aufgrund von Hibernate flushMode =>FlushMode.AUTO
. In Grails 2.4.0 trifft das nicht mehr zu. Der Standard-FlushMode der Hibernate-Sitzung in einer beliebigen Controller-Aktion oder Serviceklasse lautet FlushMode.MANUAL
.
Die Dinge werden noch seltsamer, wenn sessionFactory.currentSession.flushMode
in BootStrap abrufen, wo sie einen Wert von FlushMode.AUTO
und in einer Controller-Aktion haben, wo er den Wert FlushMode.MANUAL
hat. Dies kann überprüft werden, indem Sie eine neue Grails-App erstellen und println "flushMode = $sessionFactory.currentSession.flushMode"
in BootStrap und in einer Controller-Aktion (z. B. index()) setzen.
Ich habe in den letzten 2 Tagen alle Arten von Foren durchsucht und keine vernünftige Erklärung gefunden, warum dies in Grails 2.4.0 (oder vielleicht sogar in früheren Versionen) geändert werden musste. Ich habe nur Kommentare gefunden, die sagen, dass es riskant ist, FlushMode.MANUAL
zu haben, weil es bei der Abfrage der Datenbank zu veralteten Objekten kommen kann, nachdem andere geändert wurden.
Ich weiß, dass:
- mit
grails.gorm.autoFlush = true
in Config Sie einen Flush zwingen kann: true zu jedem Speichern() - in Hibernate3 und hibernate4 Standard-Flushmode ist
FlushMode.AUTO
- seine nicht möglich Flushmode zu setzen in Config.groovy noch in DataSource.groovy. Ich habe das alles versucht und nichts hat den Job gemacht:
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'
Bitte kann jemand ein wenig Licht in diese werfen?
Eigentlich würde ich gerne wissen, ob in Grails 2.4.0 FlushMode.MANUAL
ist jetzt der gewünschte Standard?
Und wenn ja:
- Was mit dem Kommentar ist "... Der Vorschlag ist nicht, dass wir die AUTO Spülmodus vollständig ... deaktivieren" von Peter Ledbrook in GRAILS-7180
- Was die Best Practice ist Probleme mit veralteten Objekten nicht zu lösen, insbesondere bei komplexen Manipulationen an Domänenobjekten, bei denen das Modifizieren, das Erstellen neuer Instanzen und das Abfragen gemischt sind.
Vielen Dank - Andi
Nach Graemes Antwort und seine Kommentare zu lesen, habe ich versucht, besser zu klären, was mit denen ich zu kämpfen und hat die folgenden vereinfachten Domäne und Controller-Klassen, die dieses Verhalten zeigen, :
Domain Klasse Msg:
class Msg {
String text
static constraints = {
text nullable:true
}
}
und der 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>')
}
}
http://localhost/appname/msg/save1
die Ausgabe So fordern ist:
*** flushMode when in controller/save1 = MANUAL
msg 1 created with text = hallo
Hier habe ich es nicht bekommen, warum Hibernate das Objekt bestehen bleibt, auch du flushMode ist MANUELL.
Und wenn http://localhost/appname/msg/save2
Aufruf der Ausgang ist:
*** flushMode when in controller/save2 = MANUAL
msg null created with text = hallo
Das Objekt nicht beibehalten werden kann, da Hibernate nicht einen Flush herausgibt, also nie ruft eine SQL „update ...“ -Befehl.
Aber jetzt scheint es, dass nicht nur der FlushMode ein Problem ist, sondern auch wenn man hasErrors() aufruft oder nicht! Ich bin noch mehr verwirrt ...
Wenn Sie dieses Beispiel in Grails 1.3.7 tun beide Aktionen speichern (save1 und save2) persistent das neu erstellte msg-Objekt!
Danke für Ihre Erklärung. Das macht Sinn. Aber warum wechselt Grails in einer Controller-Aktion auf MANUAL, auch wenn keine Domain-Objekte gespeichert sind (überhaupt verwendet werden)? Ich habe eine winzige Grails-App mit nur einem Controller und keiner Domain-Klasse geschrieben, um das zu überprüfen. In der Controller-Index-Aktion gebe ich nur 1 LOC 'println 'ein. FlushMode = $ sessionFactory.currentSession.flushMode" ' –
Für Leseoperationen verwendet Grails eine schreibgeschützte Transaktion. Eine schreibgeschützte Transaktion verwendet einen Löschmodus von MANUAL. Der Grund dafür, dass Lesevorgänge eine schreibgeschützte Transaktion verwenden, liegt darin, dass die Leistung verbessert wird, da Hibernate keine Lesevorgänge auf schreibgeschützte Objekte überprüfen muss. Dies kann das sein, was du siehst. –
Siehe die ursprüngliche Frage mit aktualisierten Domäne und Controller-Beispiel –