【问题标题】:Why does Grails/GORM/Hibernate hit my setters on .save() and what to do about it?为什么 Grails/GORM/Hibernate 会在 .save() 上击中我的设置​​器以及如何处理它?
【发布时间】:2025-12-27 05:30:17
【问题描述】:

我有一个包含两个变量的 DomainClass,用户只能设置一个或另一个。他们没有设置的那个是由他们设置的那个 setter 中的一些代码决定的。因此,如果他们设置 A,B 会设置我想要的,如果他们设置 B,A 会设置我想要的。我遇到的问题是 .save() GORM 或 Hibernate 或其他东西也影响了设置器。

这是我添加到名为 l2getset 的新 grails 2.0 项目中的示例域:

package l2getset

class ExampleDomain {
  String thisIsA
  String thisIsB
  void setThisIsA(String thisIsA){
    println "Hitting A Setter"
    this.thisIsA = thisIsA
    this.thisIsB = 'user set A'
  }
  void setThisIsB(String thisIsB){
    println "Hitting B Setter"
    this.thisIsB = thisIsB
    this.thisIsA = 'user set B'
  }
  static constraints = {
  }
}

还有引导程序:

import l2getset.*
class BootStrap {
  def init = { servletContext ->
    def someExample = new ExampleDomain()
    someExample.thisIsA = "Some String"

    println "Some Example is: ${someExample.thisIsA} / ${someExample.thisIsB}"
    someExample.save()
    println "Some Example is: ${someExample.thisIsA} / ${someExample.thisIsB}"  
  }
  def destroy = {
  }
}

打印:

| Running Grails application
Hitting A Setter
Some Example is: Some String / user set A
Hitting A Setter
Hitting B Setter
Some Example is: user set B / user set A
| Server running. Browse to http://localhost:8080/l2getset

我如何区分当我“合法地”设置其中之一和 GORM/Hibernate/PleaseExplain 只是“玩弄我的设置器”之前,它会保留我的东西?

这个问题:Why are setters in Grails called twice on save? 似乎也涉及到这个问题,但我仍然想知道发生了什么以及如何解决我的问题。

【问题讨论】:

    标签: hibernate grails save grails-orm setter


    【解决方案1】:

    发生这种情况是因为 hibernate 在加载时设置属性时会自动使用 setter。您想要的是在 hibernate 中对这些属性进行字段级访问。

    请参阅此处了解如何实现它。

    Grails: field access with GORM

    【讨论】:

    • 这对于我正在寻找的功能来说似乎很多。是否有某种 groovy hack 来检查它是如何被调用并确定它是否来自 hibernate.save() ?
    • 这实际上并不难。请参阅我对另一个问题的评论。
    【解决方案2】:

    执行此操作的另一种方法是将值保留在一个值中,但您使用瞬态属性作为代码与之交互的值。瞬态属性的设置器中包含您的额外代码,它还设置了 hibernate 知道的“真实”值。

    例如:

    package l2getset
    
    class ExampleDomain {
      String _thisIsA
      String _thisIsB
    
      static transients = ["thisIsA", "thisIsB"]
    
      String getThisIsA() { _thisIsA }
    
      void setThisIsA(String thisIsA){
        println "Hitting A Setter"
        this._thisIsA = thisIsA
        this._thisIsB = 'user set A'
      }
    
      String getThisIsB() { _thisIsB }
    
      void setThisIsB(String thisIsB){
        println "Hitting B Setter"
        this._thisIsB = thisIsB
        this._thisIsA = 'user set B'
      }
    }
    

    打印:

    Running Grails application..
    Hitting A Setter
    Some Example is: Some String / user set A
    Some Example is: Some String / user set A
    Server running. Browse to http://localhost:8080/transient-setter
    

    如果需要,您可以弄乱域类的映射,告诉它_thisIsA_thisIsB 字段映射到没有_ 的文件名。

    【讨论】:

    • 这是一个好主意,但它不适合我的实际问题。感谢您的建议 - 我可能会在这条路上使用它。
    【解决方案3】:

    这是我的 hack 解决方案:

    void setThisIsA 中的this.thisIsB = 'user set A' 行替换为以下堆栈跟踪检查:

    StringWriter sw = new StringWriter();
    new Throwable("").printStackTrace(new PrintWriter(sw));
    String stackTrace = sw.toString();
    if(!stackTrace.find('DefaultSaveOrUpdateEventListener')){
      this.b = 'user set A'
    }
    

    对于void setThisIsB,反之亦然

    【讨论】:

    • 增加了大约 12ms 的开销,所以我会以正确的方式来做。
    最近更新 更多