【问题标题】:Grails Overriding domain.addToGrails 覆盖 domain.addTo
【发布时间】:2016-12-01 14:31:26
【问题描述】:

当一个新对象被添加到具有 hasMany 关系的域时,我想做一些工作。

例如,对于Person hasMany Hobby,在拦截器中为addToHobby()removeFromHobby() 做一些工作,如下所示:

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

由于某种原因引发了一个错误,我猜这与 Gorm 正在完成的一些魔术工作有关,而当方法被覆盖时没有完成。无论如何围绕这个?我可以将这种东西放在 beforeUpdate 或类似的东西中,但这更通用,并且会捕获每个更新,而不仅仅是从列表中添加或删除。

请注意,抛出的错误不是7年前关于该主题的帖子中提到的空指针异常(显然是由于无法启动Set引起的),而是InvocationTargetException阻止了超级方法被调用。

    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)

【问题讨论】:

  • 不这么认为。该问题没有调用 addTo 方法的超类。
  • 没错,因为你不能。很高兴你得到了答案。
  • 我的意思是问题不一样,因此它不是骗子。接受的答案描述了实现相同目标的方法。
  • 我建议您不要更改域 addTo,而是将此代码放在您调用的服务中。

标签: grails grails-domain-class


【解决方案1】:

领域方法通过元编程或类似技术注入到您的类中,因此不存在 super.addTo*() 这样的东西。

对您而言,最简单的方法是使用您提到的 interceptors,或者您可以添加自己的元方法来覆盖 GORM 的默认 addTo

例如:

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

【讨论】:

  • 感谢 v 有帮助。如何获取调用的目标?在这种情况下,除了执行默认的 addTo 之外,我还想对相关人员进行其他更改。 Person.metaClass.blah 中的“this”是 person 实例吗?我也可以得到提供给该方法的任何参数吗?
  • 在闭包内你应该使用'delegate`而不是this。如果您println delegate,您将在输出中看到您的人员实例。查看更新
  • 非常感谢。我要花几个小时才能弄清楚。
【解决方案2】:

基于上面injecteer的评论,下面是在addTo之后添加处理程序的通用方法(或任何其他采用单个参数的动态方法)。

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

要使用它,请先在 Bootstrap 或其他地方调用 setAfterHandler 方法,然后再尝试使用它,例如

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

在您的域类中,使用方法名称 after_addToCollectionName 插入您的处理程序,如下例所示:

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

【讨论】:

    【解决方案3】:

    你可能会从另一个角度解决这个问题更有趣:...

    class Hobby {
       String name
       static belongsTo = [person: Person]
       setPerson(Person p) {
          this.person = p
          if (name="Fishing") {p.likesFishing=true;}
       }
       Person getPerson() {
          return this.person
       }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-03-25
      • 2015-12-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-07-28
      • 2015-09-13
      相关资源
      最近更新 更多