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
}