How can I duplicate a domain object in Grails?
Asked Answered
C

2

7

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?

Cristalcristate answered 12/7, 2013 at 12:20 Comment(0)
D
5

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.

Dongdonga answered 12/7, 2013 at 12:27 Comment(0)
N
8

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
}
Nablus answered 13/7, 2013 at 11:55 Comment(2)
before returning the object i tried to save it but it is showing null...how can save this new objectCypriot
Hello @roanjain, I'll review this code to check if it is updated. but I can say that I am using it successfully to clone several classes.Nablus
D
5

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.

Dongdonga answered 12/7, 2013 at 12:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.