2014-06-19 3 views
6

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!

Antwort

8

Grails setzt den Löschmodus vor der Validierung auf Manuell, um zu verhindern, dass Änderungen während der Validierung gelöscht werden (dies kann recht häufig sein, da Sie möglicherweise einen benutzerdefinierten Validierer haben, der vorhandene Daten abfragt).

Siehe 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

Wenn es irgendwelche Validierungsfehler sind, wird es nicht den Flush-Modus zurück auf AUTO eingestellt. Dies soll verhindern, dass ungültige Objekte beibehalten werden.

Was Sie sehen, ist wahrscheinlich Validierungsfehler aufgetreten und obwohl Sie einen Flush erzwingen können, ist es nicht ratsam.

+0

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" ' –

+0

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. –

+0

Siehe die ursprüngliche Frage mit aktualisierten Domäne und Controller-Beispiel –