2016-12-01 1 views
1

Je voudrais faire un peu de travail quand un nouvel objet est ajouté à un domaine avec une relation hasMany.Grails Overriding domain.addTo

comme par exemple, pour un Person hasMany Hobby faire un travail dans l'intercepteur pour addToHobby() et removeFromHobby() comme suit:

class Person { 
    String name 
    boolean likesFishing 
    static hasMany = [hobby: Hobby] 
    addToHobby(Hobby h) { 
     super.addToHobby(h)   //*throws missingMethod exception 
     if (h.name="Fishing") {this.likesFishing=true;} 
    } 
    removeFromHobby(Hobby h) { 
     super removeFromHobby(h) 
     if (h.name="Fishing") {this.likesFishing=false;} 
    } 
} 

Pour une raison quelconque une erreur est renvoyée que je suppose que a quelque chose à voir avec un travail magique étant fait par Gorm qui n'est pas fait quand la méthode est surchargée. En tout cas autour de ça? Je peux mettre ce genre de chose dans beforeUpdate ou quelque chose comme ça, mais c'est beaucoup plus général et je vais prendre toutes les mises à jour, pas seulement un ajout ou un retrait de la liste. Notez que l'erreur levée n'est pas l'exception de pointeur nul mentionnée dans le post sur ce sujet il y a 7 ans (apparemment provoqué par faiilure pour initialiser l'ensemble), mais plutôt une exception InvocationTargetException empêchant la super méthode d'être appelée.

No signature of method: testapp.Person.addToHobby() is applicable for argument types: (testapp.Hobby) values: [testapp.Hobby : (unsaved)] 
Possible solutions: addToHobby(testapp.Hobby), addToHobby(java.lang.Object), getHobby(). Stacktrace follows: 
java.lang.reflect.InvocationTargetException: null 
     at org.grails.core.DefaultGrailsControllerClass$ReflectionInvoker.invoke(DefaultGrailsControllerClass.java:210) 
     at org.grails.core.DefaultGrailsControllerClass.invoke(DefaultGrailsControllerClass.java:187) 
     at org.grails.web.mapping.mvc.UrlMappingsInfoHandlerAdapter.handle(UrlMappingsInfoHandlerAdapter.groovy:90) 
     at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:963) 
     at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:897) 
     at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970) 
     at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861) 
     at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846) 
     at org.springframework.boot.web.filter.ApplicationContextHeaderFilter.doFilterInternal(ApplicationContextHeaderFilter.java:55) 
     at org.grails.web.servlet.mvc.GrailsWebRequestFilter.doFilterInternal(GrailsWebRequestFilter.java:77) 
     at org.grails.web.filters.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:67) 
     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) 
     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) 
     at java.lang.Thread.run(Thread.java:745) 
+0

double possible de [? Comment remplacer addTo \ * et RemoveFrom \ * méthodes GORM/Grails] (ht tp: //stackoverflow.com/questions/1461857/how-to-override-addto-and-removefrom-gorm-grails-methods) – Gregg

+0

Ne le pense pas. Cette question n'a pas invoqué le super de la méthode addTo. – user1023110

+0

Exactement, parce que vous ne pouvez pas. Content que tu aies la réponse. – Gregg

Répondre

1

Les méthodes de domaine sont injectées dans vos classes par méta-programmation ou d'une technique similaire, donc il n'y a pas une telle chose comme super.addTo*(). La manière la plus simple pour vous serait d'utiliser interceptors comme vous l'avez mentionné, ou vous pouvez ajouter vos propres méta-méthodes pour remplacer les addTo par défaut de GORM.

Par exemple:

class BootStrap { 

    def init = { servletContext -> 
    def oldAddToHobby = Person.metaClass.getMetaMethod 'addToHobby' 

    Person.metaClass.addToHobby{ Hobby h -> 
     oldAddToHobby.invoke delegate, h 
     println 'blah 2' 
    }   
    } 

    def destroy = {} 
} 
+0

Merci v utile. Comment puis-je obtenir la cible de l'invocation? Dans ce cas, en plus de faire les choses addTo par défaut, je veux faire d'autres changements à la personne concernée. Est-ce 'ceci' à l'intérieur de Person.metaClass.blah l'instance de personne? Puis-je aussi me procurer les paramètres fournis avec la méthode? – user1023110

+0

à l'intérieur de la fermeture, vous devez utiliser 'delegate' au lieu de 'this'. Si vous 'println delegate' vous verrez votre instance de personne dans la sortie. Voir la mise à jour – injecteer

+0

Merci beaucoup. Cela m'aurait pris des heures pour comprendre cela. – user1023110

1

Basé sur le commentaire de injecteer ci-dessus, au-dessous est une méthode générique pour ajouter un gestionnaire après addTo (ou toute autre méthode dynamique qui prend un seul paramètre).

public class Meta { 
static boolean setAfterHandler(String methodName, Class sourceClass, Class paramClass) { 
    //get the method 
    MetaMethod mymethod = sourceClass.metaClass.getMetaMethod(methodName) 
    if (mymethod==null) return false 
    //if after_ handler exists, update the metaClass to call the 
    // dynamic method, and then the after_ method handler. r 
    if (sourceClass.newInstance().respondsTo("after_${methodName}")) { 
     sourceClass.metaClass."${methodName}" {myParam -> 
      mymethod.invoke delegate, myParam 
      delegate."after_${methodName}"(myParam); 
     } 
     return true 
    } else { 
     return false 
    } 
} 

Pour l'utiliser, appelez la méthode setAfterHandler quelque part dans Bootstrap ou ailleurs avant d'essayer de l'utiliser, par exemple

def success = Meta.setAfterHandler("addToHobby", Person.class, Hobby.class) 

Dans votre classe de domaine, insérez votre gestionnaire avec le nom de la méthode after_addTo CollectionName comme dans l'exemple ci-dessous:

class Person { 

    String first 
    String last 
    Integer age 
    boolean likesFishing=false 

    static hasMany = [hobby: Hobby] 

    static constraints = { 

    } 

    void after_addToHobby(Hobby h) { 
     if (h.name=="Fishing") { 
      this.likesFishing=true; 
     } 
    } 
}