2009-12-24 8 views
7

Ich schreibe ein Grails-Plugin und ich muss in die Domain save() -Methode einhängen, um nach dem Speichern etwas Logik zu machen. Ich muss dies über mehrere Domänenklassen hinweg tun. Ich versuche, Hibernate-Ereignisse in den Fällen zu vermeiden, in denen ein Plug-in-Benutzer nicht Hibernate mit GORM verwendet.Einhängen in Grails Domain-Objekt save()

Ich habe viele Dinge ausprobiert, aber unten ist, was ich denke, sollte die beste Chance bei der Arbeit gehabt haben. In allen Fällen grailsSave ist null. Wie kann ich das machen?

def doWithDynamicMethods = { ctx -> 
    application.domainClasses.each { dc -> 
     def grailsSave = dc.metaClass.pickMethod('save', [Map] as Class[]) 

     domainClass.metaClass.save = { Map params -> 
     grailsSave.invoke(delegate, [params] as Object[]) 
     println "Saved object, now do my thing" 
     //... 
     } 
    } 
} 

ich in meiner * Plugin.groovy Klasse folgende Einstellungen haben:

def dependsOn = [domainClass: '1.1 > *', hibernate: '1.1 > *'] 
def loadAfter = ['hibernate'] 

Antwort

6

Ich konnte keinen Verweis auf die save() -Methoden während der Plugin/App-Initialisierung erhalten; Ich weiß nicht warum. Stattdessen habe ich entschieden, nach dem Einfügen, Aktualisieren und Löschen einen Listener für die Hibernate-Ereignisse zu erstellen. Diese post von Sean Hartsock in Bezug auf das Audit Logging-Plugin war ein perfekter Grund dafür.

Hier ist der Kern der Zuhörer:

class MyListener implements PostInsertEventListener, PostUpdateEventListener, PostDeleteEventListener, Initializable { 

     public void onPostInsert(final PostInsertEvent event) { 
      // logic after insert 
      return 
     } 

     public void onPostUpdate(final PostUpdateEvent event) { 
      // logic after update 
      return 
     } 

     public void onPostDelete(final PostDeleteEvent event) { 
      // logic after delete 
      return 
     } 


     public void initialize(final Configuration config) { 
      return 
     } 
    } 

Dann in der * GrailsPlugin.groovy:

def doWithApplicationContext = { applicationContext -> 

    // add the event listeners for reindexing on change 
    def listeners = applicationContext.sessionFactory.eventListeners 
    def listener = new MyListener() 

    ['postInsert', 'postUpdate', 'postDelete'].each({ 
     addEventTypeListener(listeners, listener, it) 
    }) 

} 


// copied from http://hartsock.blogspot.com/2008/04/inside-hibernate-events-and-audit.html 
private addEventTypeListener(listeners, listener, type) { 
    def typeProperty = "${type}EventListeners" 
    def typeListeners = listeners."${typeProperty}" 

    def expandedTypeListeners = new Object[typeListeners.length + 1] 
    System.arraycopy(typeListeners, 0, expandedTypeListeners, 0, typeListeners.length) 
    expandedTypeListeners[-1] = listener 

    listeners."${typeProperty}" = expandedTypeListeners 
} 

ziemlich einfach am Ende des Tages ...

+0

Shawn und sein Audit Logging-Plugin rockt. –

+0

Danke fürs Teilen! Ich war zu faul, um das selbst herauszufinden. – Kimble

1

Wäre dies der Serviceklasse werden nicht am besten aufgenommen, die die Einheit der Arbeit besitzt? Dort würde das übliche Spring/Grails-Idiom eine solche Logik haben. Sie müssen den Speichervorgang überhaupt nicht ändern.

+0

Obwohl ich ein pluging bin, um von anderen verwendet zu werden. Meine Logik mag sehr gut in einem Dienst sein, aber der Punkt, den sie auslösen muss, ist beim Speichern. Danke – mbrevoort

+0

Die Idee, eine Menge Logik, die sich direkt auf die Domäne bezieht, an eine andere Stufe zu delegieren, ist einer der größten Ärgernisse im Frühling, und deshalb hat Java den Ruf, die Komplexität zu fetischisieren. –

+0

Wie kann ein einzelnes Domänenobjekt wissen, wann es Teil einer größeren Arbeitseinheit ist und wann nicht? – duffymo

2

Es gibt drei verschiedene Version zum metaclass hinzugefügt Speichern,

save(Map) 
save(Boolean) 
save() 

Welches sind Sie in Ihrem Test nennen? Sie müssen jedem Code hinzufügen.

Eine andere Sache ist zu prüfen, ob das Plugin nach dem Hibernate-Plugin läuft, die die drei Methoden, um die

Lee

+0

Danke Lee, Ich glaube, ich habe Probleme, das Plugin nach dem Winterschlaf zu zwingen. Wenn ich in doWithDynamicMethods starte, sind alle Versuche, pickMethod aufzurufen, null. Wenn ich in der Konsole von Grails laufe, sind sie nicht null. Ich habe das in meinem Plugin aber kein Glück: def dependsOn = [domainClass: '1.1> *', Ruhezustand: '1.1> *'] def loadAfter = ['überwintern'] – mbrevoort

+0

Hmmm loadAfter ist der Sie wollen - muss es sein static loadAfter = ['hibernate'] – leebutts

+0

statisch hat auch nicht funktioniert. Seltsamerweise habe ich gerade bemerkt, dass Robert Fischer in seinem GORM Labs-Plugin versucht hatte, ähnliche Dinge mit save() zu tun, aber den Code in früheren Versionen kommentiert hatte und in der letzten Version nicht existierte. Irgendwelche Ideen, wie man das anders angehen könnte? – mbrevoort

2

Haben Sie einen Blick auf die Falcone Util Plugin

prost Metaklasse erstellt. Mit diesem Plugin können Sie sich in Hibernate-Ereignisse einklinken (siehe Dokumentation unten auf der Seite). Ich weiß nicht, ob das genau das ist, was Sie wollen, aber Sie könnten ein paar Hinweise bekommen.

Ps! Ich glaube nicht, dass das Plugin noch mit Grails 1.2 funktioniert.

+0

Danke Kumpel, dieses Plugin sieht ausgezeichnet aus. Wäre toll, wenn diese Art von Event-Fähigkeit im Kern verfügbar wäre. – mbrevoort

2

Dies ist ein Problem der vorzeitigen Optimierung: ältere Versionen von Groovy haben MetaClass-Mangling ernstlich bestraft, und so fügt GORM nicht all seine Magie hinzu, bis es die Notwendigkeit erkennt.

Die einfachste Lösung ist, Ihr Plugin dependOn GORM Labs zu haben (ich arbeite da drum herum). Die alternative Lösung besteht darin, methodMissing manuell auszulösen (was die Arbeit, die ich gemacht habe, zu duplizieren). Einzelheiten dazu finden Sie in der GORM Labs-Dokumentation.

1

Zusätzliche GORM-Methoden werden beim ersten Aufruf einer dieser Methoden langsam initialisiert. Um sie in doWithDynamicMethods initialisieren einfach eine der statischen Methoden auf Ihrer Domain-Klasse aufrufen (n):

def doWithDynamicMethods = { ctx -> 

    application.domainClasses.each { dc -> 

     // call any static method to initialize dynamic gorm methods 
     dc.clazz.count() 

     def grailsSave = dc.metaClass.pickMethod('save', [Map] as Class[]) 
     //... 
    } 
} 

Ihre save() Methode jetzt zur Verfügung stehen wird. Da dies beim Start aufgerufen wird, sollte eine einzige Zählung kein großes Problem darstellen.