2013-11-26 2 views
6

У меня есть несколько классов домена, для которых пользовательский интерфейс включает в себя «дублируемую» команду. В рамках реализации этих команд я применил методы clone() в соответствующих классах домена.Правильная реализация clone() Для доменных классов для дублирования экземпляра домена Grails

Я пытался исправить мою дурную привычку неправильно реализации клон() (в целом), основанный на использовании «new», а не "super.clone()," так, как только я думал о делать то же самое для моего домена Grails классов, я задавался вопросом, как использовать super.clone() для получения клона может взаимодействовать с GORM/Сохранение спящего режима. В частности, я был , задаваясь вопросом о правильном способе обработки неявного свойства «id». Если Я просто super.clone(), ничего не делайте дальше и позже пытайтесь сделать save() клонированный экземпляр , будет ли он работать исправно (создание новой записи ?) Или будет какой-то результат ошибки или молчания?

Каков правильный или предпочтительный способ дублирования домена Grails экземпляр?

ответ

12

Добавьте следующий метод в metaClass интерфейса GormInstanceApi, который реализует весь домен. :

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

затем:

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

Congrats! Теперь вы можете использовать clone метод, как save, delete .....

использования:

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

UPDATE: вы можете обходить все классы домена также:

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

Это выглядит как отличное решение. Но где я могу найти интерфейс GormInstanceApi.groovy, чтобы внести предлагаемое изменение? Используя Grails 2.3.4 в окне Winodws, искали везде и не могли найти его (кроме того, что я нахожу его на [github] (https://github.com/spring-projects/grails-data-mapping/blob/master/grails -datastore-gorm/src/main/groovy/org/grails/datastore/gorm/GormInstanceApi.groovy)). И, где именно я бы поставил метод: Object deepClone (domainInstanceToClone) ??? БЛАГОДАРЯ! – Twelve24

+1

Хорошая особенность, Поздравляем –

+2

@ Двенадцать24: этот код должен быть помещен в conf/BootStrap.groovy: здесь: \t def init = {servletContext -> –

10

У меня было гораздо больше повезло с этим методом:

YourDomainClass clonedObject = new YourDomainClass(objectToClone.properties) 
+1

Остерегайтесь: это будет использовать привязку данных Grails, идентичную вызову конструктора с помощью карты. Если свойство неприменимо, то оно ** не будет скопировано **. –

Смежные вопросы