Cloning an instance of domain in Grails

坚强是说给别人听的谎言 提交于 2019-12-11 12:26:22

问题


I'm wondering how I would go about adding the functionality of cloning to my grails application. I've attached an image below that explains how my domain classes are associated. One template has many steps and those steps each have many inputs and or outputs.

Currently I can view my templates on the index.gsp page but I want to be able to clone entire templates along with their steps/inputs/outputs that they contain aswell.

Is this possible and if so how?


回答1:


Here is a version of deep cloning. Though It's a bit customized to meet specific needs, it's very generic. And I'm pretty sure above said scenario is well covered by this.

 Object deepClone(def domainInstanceToClone, def notCloneable) {
        return deepClone(domainInstanceToClone, notCloneable, null)
    }

Object deepClone(def domainInstanceToClone) {
    return deepClone(domainInstanceToClone, null, null)
}

Object deepClone(def domainInstanceToClone, def notCloneable, def bindOriginal) {

    if (domainInstanceToClone.getClass().name.contains("_javassist"))
        return null

    //Our target instance for the instance we want to clone
    def newDomainInstance = domainInstanceToClone?.getClass()?.newInstance()

    //Returns a DefaultGrailsDomainClass (as interface GrailsDomainClass) for inspecting properties
    GrailsClass domainClass = domainInstanceToClone.domainClass.grailsApplication.getDomainClass(newDomainInstance.getClass().name)

    for (DefaultGrailsDomainClassProperty prop in domainClass?.getPersistentProperties()) {
        if (notCloneable && prop.name in notCloneable) {
            continue
        }
        if (bindOriginal && prop.name in bindOriginal) {
            newDomainInstance."${prop.name}" = domainInstanceToClone."${prop.name}"
            continue
        }

        if (prop.association) {
            if (prop.owningSide) {
                //we have to deep clone owned associations
                if (prop.oneToOne) {
                    def newAssociationInstance = deepClone(domainInstanceToClone?."${prop.name}", notCloneable, bindOriginal)
                    newDomainInstance."${prop.name}" = newAssociationInstance
                } else {
                    domainInstanceToClone."${prop.name}".each { associationInstance ->
                        def newAssociationInstance = deepClone(associationInstance, notCloneable, bindOriginal)

                        if (prop.oneToMany) {
                            if (newAssociationInstance) {
                                newDomainInstance."addTo${prop.name.capitalize()}"(newAssociationInstance)
                            }
                        } else {
                            newDomainInstance."${prop.name}" = 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, notCloneable, bindOriginal)
                            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 == "activationDate") {
                newDomainInstance."${prop.name}" = new Date()
            }
        }
    }
    return newDomainInstance
}

Example usage is :-

Template cloneTemplate = cloneService.deepClone(originalTemplate,["id","name"],["parent"])

1st parameter is original object that is to be cloned 2nd parameter is the list of columns that must not be cloned 3rd parameter is list of properties that must be referenced as it is.e.g. Template might belong to some parent which must remain same during clone.

To save cloned object create another method that meets your custom requirements.Above code will work in other scenarios too.



来源:https://stackoverflow.com/questions/27217445/cloning-an-instance-of-domain-in-grails

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!