Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I duplicate a domain object in Grails?

I want to make a copy of a domain object. What is the simplest way to accomplish this?

I realize I could create a new record, and then iterate over each of the fields copying the data field-by-field - but I figured there must be an easier way to do this...

In Rails there is a simple way to do this:

#rails < 3.1
new_record = old_record.clone

#rails >= 3.1
new_record = old_record.dup

Is there any equivalent in Grails?

like image 426
Eduard Avatar asked Jul 12 '13 12:07

Eduard


2 Answers

I've adapted a piece of code that make the deep clone of domain classes. I've been using in my system and it works very well (in most of cases). The code bellow is an adaptation found in http://grails.1312388.n4.nabble.com/Fwd-How-to-copy-properties-of-a-domain-class-td3436759.html

In my application the user has the option to saveAs some type of objects and I use the deepClone to do that.

You can specify "not cloneable" properties. For that you need to specify a static map (in your class) with the properties that you don't want to clone, for example:

static notCloneable = ['quoteFlows','services']
static hasMany = [quotePacks: QuotePack, services: Service, clients: Client, quoteFlows: QuoteFlow]


static 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
}
like image 183
cantoni Avatar answered Nov 20 '22 04:11

cantoni


There is not. It has been requested http://jira.grails.org/browse/GRAILS-3532. Someone has added some code to that issue that might be helpful to you though.

like image 38
Jeff Storey Avatar answered Nov 20 '22 04:11

Jeff Storey