Search code examples
grailsgrails-domain-class

Grails Overriding domain.addTo


I'd like to do some work when a new object is added to a domain with a hasMany relationship.

like for example, for a Person hasMany Hobby do some work in the interceptor for addToHobby() and removeFromHobby() as follows:

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;}
   }
}

For some reason an error is thrown which I guess has something to do with some magic work being done by Gorm that isn't done when the method is overridden. Anyway around this? I can put this kind of thing in beforeUpdate or something like that, but that's a lot more general and will catch every update, not just an addition or removal from the list.

Note the error thrown is not the null pointer exception referred to in the post on this topic 7 years ago (apparently caused by faiilure to initiate the Set), but rather a InvocationTargetException preventing the super method from beeing called.

    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)

Solution

  • The domain methods are injected into your classes by meta-programming or similar technique, so there's no such thing as super.addTo*().

    The easiest way for you would be to use interceptors as you mentioned, or you can add your own meta-methods to override GORM's default addTo's.

    For example:

    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 = {}
    }