问题
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