正确实现clone()对于域类来复制Grails域实例

Abd*_*UMI 6 grails clone grails-orm

我有几个域类,其用户界面包含"重复"命令.作为这些命令的实现的一部分,我clone()在相应的域类中实现了方法.

我一直试图纠正我不正当地实现clone()(一般)基于使用" new"的坏习惯,而不是"super.clone()," 一旦我想为我的Grails域类做同样的事情,我想知道如何使用super.clone()获取一个clone可能与GORM/ Hibernate持久性交互.特别是,我想知道处理隐式"id"属性的正确方法.如果我只是super.clone(),不做任何事情,稍后尝试save()克隆实例,它会正常工作(创建一个新的持久性条目吗?)或会出现某种错误或无声失败的结果?

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

Abd*_*UMI 15

将以下方法添加到GormInstanceApi所有域实现它的接口的metaClass .:

def cloneForDomains={def cloned=delegate.class.newInstance();
                 cloned.properties=delegate.properties;
                return cloned;}
Run Code Online (Sandbox Code Playgroud)

然后 :

org.grails.datastore.gorm.GormInstanceApi.clone=cloneForDomains ;
Run Code Online (Sandbox Code Playgroud)

恭喜!现在你可以使用clone的方法,例如save,delete.....

使用案例:

Person p=Person.get(1);
Person cloned=p.clone(); 
 cloned.id=null; 
cloned.save();
Run Code Online (Sandbox Code Playgroud)

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

grailsApplication.getDomainClasses().each{cls->
             cls.metaClass.clone=cloneForDomains
         }
Run Code Online (Sandbox Code Playgroud)

更新:深度克隆:

  grailsApplication.getDomainClasses().each{cls->
                 cls.metaClass.clone={
                                  return deepClone(delegate);   
                            }
   }
Run Code Online (Sandbox Code Playgroud)

已知这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
     }
Run Code Online (Sandbox Code Playgroud)

  • @ Twelve24:此代码应放在conf/BootStrap.groovy中:here:def init = {servletContext - > (2认同)

Dus*_*tin 10

我用这个方法运气得多:

YourDomainClass clonedObject = new YourDomainClass(objectToClone.properties)
Run Code Online (Sandbox Code Playgroud)

  • 注意:这将使用 Grails 数据绑定,与使用 Map 调用构造函数相同。如果一个属性是不可绑定的,那么它将**不会**被复制。 (2认同)