【问题标题】:Proper Implementation of clone() For Domain Classes to duplicate a Grails domain instance域类复制 Grails 域实例的 clone() 的正确实现
【发布时间】:2013-11-26 15:05:35
【问题描述】:

我有几个域类,用户界面包括 “重复”命令。作为执行这些命令的一部分, 我已经在相应的域类中实现了clone() 方法。

我一直在努力改正我执行不当的坏习惯 clone()(通常)基于使用“new”而不是"super.clone()," 所以一旦我想到对我的 Grails 域做同样的事情 类,我想知道如何使用super.clone() 获取克隆可能 与GORM/Hibernate 持久化交互。特别是,我是 想知道处理隐式“id”属性的正确方法。如果 我只是super.clone(),什么都不做,以后试试save() 克隆的实例,它会正常工作吗(创建一个新的持久性 条目?)还是会导致某种错误或静默失败?

复制 Grails 域的正确或首选方法是什么 实例?

【问题讨论】:

    标签: grails clone grails-orm


    【解决方案1】:

    将以下方法添加到接口GormInstanceApi的元类中,所有域都实现了它。 :

    def cloneForDomains={def cloned=delegate.class.newInstance();
                     cloned.properties=delegate.properties;
                    return cloned;}
    

    然后:

    org.grails.datastore.gorm.GormInstanceApi.clone=cloneForDomains ;
    

    恭喜!现在您可以使用clone 方法,例如savedelete .....

    用例:

    Person p=Person.get(1);
    Person cloned=p.clone(); 
     cloned.id=null; 
    cloned.save();
    

    更新:您也可以循环所有域类:

    grailsApplication.getDomainClasses().each{cls->
                 cls.metaClass.clone=cloneForDomains
             }
    

    更新: 用于深度克隆:

      grailsApplication.getDomainClasses().each{cls->
                     cls.metaClass.clone={
                                      return deepClone(delegate);   
                                }
       }
    

    知道deepClone是一个方法如下:

    Object deepClone(domainInstanceToClone) {
    
             //TODO: PRECISA ENTENDER ISSO! MB-249 no youtrack
             //Algumas classes chegam aqui com nome da classe + _$$_javassist_XX
             if (domainInstanceToClone.getClass().name.contains("_javassist"))
                 return null
    
             //Our target instance for the instance we want to clone
             // recursion
             def newDomainInstance = domainInstanceToClone.getClass().newInstance()
    
             //Returns a DefaultGrailsDomainClass (as interface GrailsDomainClass) for inspecting properties
             GrailsClass domainClass = domainInstanceToClone.domainClass.grailsApplication.getDomainClass(newDomainInstance.getClass().name)
    
             def notCloneable = domainClass.getPropertyValue("notCloneable")
    
             for(DefaultGrailsDomainClassProperty prop in domainClass?.getPersistentProperties()) {
                 if (notCloneable && prop.name in notCloneable)
                     continue
    
                 if (prop.association) {
    
                     if (prop.owningSide) {
                         //we have to deep clone owned associations
                         if (prop.oneToOne) {
                             def newAssociationInstance = deepClone(domainInstanceToClone?."${prop.name}")
                             newDomainInstance."${prop.name}" = newAssociationInstance
                         } else {
    
                             domainInstanceToClone."${prop.name}".each { associationInstance ->
                                 def newAssociationInstance = deepClone(associationInstance)
    
                                 if (newAssociationInstance)
                                     newDomainInstance."addTo${prop.name.capitalize()}"(newAssociationInstance)
                             }
                         }
                     } else {
    
                         if (!prop.bidirectional) {
    
                             //If the association isn't owned or the owner, then we can just do a  shallow copy of the reference.
                             newDomainInstance."${prop.name}" = domainInstanceToClone."${prop.name}"
                         }
                         // @@JR
                         // Yes bidirectional and not owning. E.g. clone Report, belongsTo Organisation which hasMany
                         // manyToOne. Just add to the owning objects collection.
                         else {
                             //println "${prop.owningSide} - ${prop.name} - ${prop.oneToMany}"
                             //return
                             if (prop.manyToOne) {
    
                                 newDomainInstance."${prop.name}" = domainInstanceToClone."${prop.name}"
                                 def owningInstance = domainInstanceToClone."${prop.name}"
                                 // Need to find the collection.
                                 String otherSide = prop.otherSide.name.capitalize()
                                 //println otherSide
                                 //owningInstance."addTo${otherSide}"(newDomainInstance)
                             }
                             else if (prop.manyToMany) {
                                 //newDomainInstance."${prop.name}" = [] as Set
    
                                 domainInstanceToClone."${prop.name}".each {
    
                                     //newDomainInstance."${prop.name}".add(it)
                                 }
                             }
    
                             else if (prop.oneToMany) {
                                 domainInstanceToClone."${prop.name}".each { associationInstance ->
                                     def newAssociationInstance = deepClone(associationInstance)
                                     newDomainInstance."addTo${prop.name.capitalize()}"(newAssociationInstance)
                                 }
                             }
                         }
                     }
                 } else {
                     //If the property isn't an association then simply copy the value
                     newDomainInstance."${prop.name}" = domainInstanceToClone."${prop.name}"
    
                     if (prop.name == "dateCreated" || prop.name == "lastUpdated") {
                         newDomainInstance."${prop.name}" = null
                     }
                 }
             }
    
             return newDomainInstance
         }
    

    【讨论】:

    • 这看起来是一个很好的解决方案。但是,我在哪里可以找到 GormInstanceApi.groovy 接口来进行建议的更改?在 Winodws 框上使用 Grails 2.3.4,到处搜索却找不到它(除了我在 github 上找到它)。而且,我将把方法放在哪里: Object deepClone(domainInstanceToClone) ???谢谢!
    • 好功能,恭喜
    • @Twelve24 : 这段代码应该放在 conf/BootStrap.groovy :here : def init = { servletContext ->
    • 为什么我们会自动拒绝任何名称中包含“_javassist”的类?谢谢!
    【解决方案2】:

    这种方法让我更幸运:

    YourDomainClass clonedObject = new YourDomainClass(objectToClone.properties)
    

    【讨论】:

    • 注意:这将使用 Grails 数据绑定,与使用 Map 调用构造函数相同。如果一个属性是不可绑定的,那么它将不会被复制。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-22
    相关资源
    最近更新 更多