Grails3中一对多域的深度拷贝
我不知道该如何实现,或者它是否真的可能/合适。我的同事和我正在使用Grails3为客户机构建一个web应用程序。他创建了最初的域名,我猜这几乎是移动应用程序领域模型的一对一副本。后来我对它们进行了修改,试图让某种形式的深度克隆工作起来,因为三个域之间有一对多的关系 问题 我将如何着手创建域的深度副本?我尝试过建议的答案,但收效甚微:Grails3中一对多域的深度拷贝,grails,gorm,Grails,Gorm,我不知道该如何实现,或者它是否真的可能/合适。我的同事和我正在使用Grails3为客户机构建一个web应用程序。他创建了最初的域名,我猜这几乎是移动应用程序领域模型的一对一副本。后来我对它们进行了修改,试图让某种形式的深度克隆工作起来,因为三个域之间有一对多的关系 问题 我将如何着手创建域的深度副本?我尝试过建议的答案,但收效甚微: 从各个地方收集想法,我来制定一个clone(Domain)方法,如下所示。它几乎可以工作(我想),但在集合抛出一个hibernateeexception-
clone(Domain)
方法,如下所示。它几乎可以工作(我想),但在集合抛出一个hibernateeexception-Found对集合的共享引用时有问题:Location.equipments
在控制器中调用为:
def copy() {
Survey.clone(Survey.get(params.id))
redirect action: 'index'
}
有什么想法或指导吗
目前,域如下所示:
class Survey {
int id
String name
String contactName
String contactEmail
String facilityAddress
String facilityCity
String facilityStateProvince
String facilityZip
String distributorName
String distributorEmail
String distributorPhoneNumber
static Survey clone(Survey self) {
Survey clone = new Survey()
String exclude = "locations"
clone.properties = self.properties.findAll {
it.key != exclude
}
self.locations.each {
Location copy = Location.clone it
clone.addToLocations copy
}
clone.save()
}
static transients = ['clone']
static belongsTo = User
static hasMany = [locations: Location]
}
克隆方法可能有问题:克隆“所有”属性,包括ID,这对于深度克隆是个坏主意。解释当一个对象与hibernate缓存中的另一个对象具有相同的属性,但具有另一个引用时,会引发错误
因此,您只需将对象的
id
属性设置为null
(或将其从属性副本中排除),即可强制hibernate检测到它是新对象。如果仍不起作用,请在保存子实体之前,在对象上调用该方法。不要这样做,只保存实体调查(根)。其他将通过级联保存
另一方面,正如@Joch所说,在这种情况下,使用克隆不是正确的方法
您应该为您的实体创建一个重复的方法。下面是一个如何克隆此结构类型的示例。这是一个有n个问题的测试,每个问题有n个答案,每堂课都有一个“复制”方法
class Test {
String name
static hasMany = [
/**
* Each question of the test
*/
questions: Question
]
/**
* Duplicates this test
*/
Test duplicate(){
Test test = new Test(name:this.name)
this.questions.each{ question ->
test.addToQuestions(question.duplicate(test))
}
test
}
}
class Question {
Integer questionOrder
String title
/**
* Each question belong to a Test
*/
static belongsTo = [test:Test]
static hasMany = [
/**
* Each answer of the test
*/
answers: Answer
]
/**
* Duplicates this test to another edition
* @param edition to be duplicated
* @return duplicated question
*/
Question duplicate(Test test){
if(test){
Question question = new Question(title:this.title)
this.answers.each{ answer->
question.addToAnswers(answer.duplicate())
}
test.addToQuestions(question)
question
}
}
}
class Answer {
String title
boolean correct
/**
* Each answer belongs to a question
*/
static belongsTo = [question:Question]
/**
* Duplicates this answer to another question
* @param edition to be duplicated
* @return duplicated question
*/
Answer duplicate(){
Answer answer = new Answer()
answer.properties['title','correct'] = this.properties['title','answerOrder','correct']
answer
}
}
在Answer.duplicate()中,您有一个如何从其他对象绑定某些属性的示例。差不多一年了,我已经完成了这个项目,并找到了解决这个问题的方法 我想出的解决办法是利用。我为每个域定义了一个服务。需要深度复制集合的任何域,称为其关联的服务方法。我只是发布了两个服务的来源,因为其他方法基本上是相同的 流程如下:
duplicate.properties=original.properties
复制所有“基本”属性,如String
、Boolean
等hibernateeexception
。因此,将集合设置为null
service/SurveyService.groovy
class SurveyService {
/**
* Attempts to perform a deep copy of a given survey
*
* @param survey The survey instance to duplicate
* @return The duplicated survey instance
*/
Survey duplicateSurvey(Survey originalSurvey) {
Survey duplicatedSurvey = new Survey()
duplicatedSurvey.properties = originalSurvey.properties
duplicatedSurvey.locations = null
duplicatedSurvey.uuid = UUIDGenerator.createUniqueId()
duplicatedSurvey.dateModified = DateUtil.getCurrentDate()
duplicatedSurvey.name = "${originalSurvey.name.replace("(copy)", "").trim()} (copy)"
duplicatedSurvey.save()
duplicatedSurvey.locations = duplicateLocations originalSurvey.locations, duplicatedSurvey
duplicatedSurvey.save()
}
/**
* Attempts to perform a deep copy of a survey's location
*
* @param originalLocations The original location set
* @param duplicatedSurvey The duplicated survey that each survey will belong to
* @return The duplicated location set
*/
Set<Location> duplicateLocations(Set<Location> originalLocations, Survey duplicatedSurvey) {
Set<Location> duplicatedLocations = []
for (originalLocation in originalLocations) {
duplicatedLocations << locationService.duplicateLocation(originalLocation, duplicatedSurvey)
}
duplicatedLocations
}
}
class LocationService {
/**
* Performs a deep copy of a given location. The duplicated location name is
* the original location name and the duplicated location ID.
*
* @param originalLocation The location to duplicate
* @param survey The survey that the location will belong to
* @return The duplicated location
*/
Location duplicateLocation(Location originalLocation, Survey survey = null) {
Location duplicatedLocation = new Location()
duplicatedLocation.properties = originalLocation.properties
duplicatedLocation.survey = survey ?: duplicatedLocation.survey
duplicatedLocation.uuid = UUIDGenerator.createUniqueId()
duplicatedLocation.dateModified = DateUtil.currentDate
duplicatedLocation.equipments = null
duplicatedLocation.products = null
duplicatedLocation.save()
duplicatedLocation.name = "${originalLocation.name.replace("(copy)", "").trim()} (copy)"
duplicatedLocation.equipments = duplicateEquipment originalLocation.equipments, duplicatedLocation
duplicatedLocation.products = duplicateProducts originalLocation, duplicatedLocation
duplicatedLocation.save()
duplicatedLocation
}
/**
* Performs a deep copy of a given locations equipments.
*
* @param originalEquipments The original locations equipments
* @param duplicatedLocation The duplicated location; needed for belongsTo association
* @return The duplicated equipment set.
*/
Set<Equipment> duplicateEquipment(Set<Equipment> originalEquipments, Location duplicatedLocation) {
Set<Equipment> duplicatedEquipments = []
for (originalEquipment in originalEquipments) {
Equipment duplicatedEquipment = new Equipment()
duplicatedEquipment.properties = originalEquipment.properties
duplicatedEquipment.uuid = UUIDGenerator.createUniqueId()
duplicatedEquipment.dateModified = DateUtil.currentDate
duplicatedEquipment.location = duplicatedLocation
duplicatedEquipment.extras = null
duplicatedEquipment.save()
duplicatedEquipment.name = "${originalEquipment.name.replace("(copy)", "").trim()} (copy)"
duplicatedEquipment.extras = duplicateExtras originalEquipment.extras, duplicatedEquipment
duplicatedEquipments << duplicatedEquipment
}
duplicatedEquipments
}
/**
* Performs a deep copy of a given locations extras.
*
* @param originalExtras The original location extras
* @param duplicatedEquipment The duplicated equipment; needed for belongsTo association
* @return The duplicated extras set.
*/
Set<EquipmentQuestionExtra> duplicateExtras(Set<EquipmentQuestionExtra> originalExtras, Equipment duplicatedEquipment) {
Set<EquipmentQuestionExtra> duplicatedExtras = []
for (originalExtra in originalExtras) {
EquipmentQuestionExtra duplicatedExtra = new EquipmentQuestionExtra()
duplicatedExtra.properties = originalExtra.properties
duplicatedExtra.equipment = duplicatedEquipment
duplicatedExtra.uuid = UUIDGenerator.createUniqueId()
duplicatedExtra.dateModified = DateUtil.currentDate
duplicatedExtra.save()
duplicatedExtras << duplicatedExtra
}
duplicatedExtras
}
/**
* Performs a deep copy of a given locations products.
*
* @param originalLocation The original location
* @param duplicatedLocation The duplicated location
* @return The duplicated product set.
*/
Set<RecommendedProduct> duplicateProducts(Location originalLocation, Location duplicatedLocation) {
Set<RecommendedProduct> originalProducts = originalLocation.products
Set<RecommendedProduct> duplicatedProducts = []
for (originalProduct in originalProducts) {
RecommendedProduct duplicatedProduct = new RecommendedProduct()
duplicatedProduct.properties = originalProduct.properties
duplicatedProduct.location = duplicatedLocation
duplicatedProduct.uuid = UUIDGenerator.createUniqueId()
duplicatedProduct.dateModified = DateUtil.currentDate
duplicatedProduct.save()
duplicatedProducts << duplicatedProduct
}
duplicatedProducts
}
}
您正在进行的克隆应该是克隆每个对象,而不考虑其类型。因此,还要克隆集合。使用
if
块检查null的目的是什么?这对我来说没有意义,因为它是唯一具有签名复制(域)
的方法。我一直在玩弄你的建议,但是它会在集合上抛出null异常。很抱歉,我清理了一个真实的代码,并且忘记删除该行。我看到你正在保存子实体。不要这样做,只保存实体调查。其他人会因为为我工作而得救。非常感谢,我在这个问题上浪费了大约半天的时间。
class EquipmentQuestionExtra {
int id
String questionText
String comment
byte[] picture
static belongsTo = Equipment
static constraints = {
picture(maxSize: 1024 * 1024)
}
}
class Test {
String name
static hasMany = [
/**
* Each question of the test
*/
questions: Question
]
/**
* Duplicates this test
*/
Test duplicate(){
Test test = new Test(name:this.name)
this.questions.each{ question ->
test.addToQuestions(question.duplicate(test))
}
test
}
}
class Question {
Integer questionOrder
String title
/**
* Each question belong to a Test
*/
static belongsTo = [test:Test]
static hasMany = [
/**
* Each answer of the test
*/
answers: Answer
]
/**
* Duplicates this test to another edition
* @param edition to be duplicated
* @return duplicated question
*/
Question duplicate(Test test){
if(test){
Question question = new Question(title:this.title)
this.answers.each{ answer->
question.addToAnswers(answer.duplicate())
}
test.addToQuestions(question)
question
}
}
}
class Answer {
String title
boolean correct
/**
* Each answer belongs to a question
*/
static belongsTo = [question:Question]
/**
* Duplicates this answer to another question
* @param edition to be duplicated
* @return duplicated question
*/
Answer duplicate(){
Answer answer = new Answer()
answer.properties['title','correct'] = this.properties['title','answerOrder','correct']
answer
}
}
class SurveyService {
/**
* Attempts to perform a deep copy of a given survey
*
* @param survey The survey instance to duplicate
* @return The duplicated survey instance
*/
Survey duplicateSurvey(Survey originalSurvey) {
Survey duplicatedSurvey = new Survey()
duplicatedSurvey.properties = originalSurvey.properties
duplicatedSurvey.locations = null
duplicatedSurvey.uuid = UUIDGenerator.createUniqueId()
duplicatedSurvey.dateModified = DateUtil.getCurrentDate()
duplicatedSurvey.name = "${originalSurvey.name.replace("(copy)", "").trim()} (copy)"
duplicatedSurvey.save()
duplicatedSurvey.locations = duplicateLocations originalSurvey.locations, duplicatedSurvey
duplicatedSurvey.save()
}
/**
* Attempts to perform a deep copy of a survey's location
*
* @param originalLocations The original location set
* @param duplicatedSurvey The duplicated survey that each survey will belong to
* @return The duplicated location set
*/
Set<Location> duplicateLocations(Set<Location> originalLocations, Survey duplicatedSurvey) {
Set<Location> duplicatedLocations = []
for (originalLocation in originalLocations) {
duplicatedLocations << locationService.duplicateLocation(originalLocation, duplicatedSurvey)
}
duplicatedLocations
}
}
class LocationService {
/**
* Performs a deep copy of a given location. The duplicated location name is
* the original location name and the duplicated location ID.
*
* @param originalLocation The location to duplicate
* @param survey The survey that the location will belong to
* @return The duplicated location
*/
Location duplicateLocation(Location originalLocation, Survey survey = null) {
Location duplicatedLocation = new Location()
duplicatedLocation.properties = originalLocation.properties
duplicatedLocation.survey = survey ?: duplicatedLocation.survey
duplicatedLocation.uuid = UUIDGenerator.createUniqueId()
duplicatedLocation.dateModified = DateUtil.currentDate
duplicatedLocation.equipments = null
duplicatedLocation.products = null
duplicatedLocation.save()
duplicatedLocation.name = "${originalLocation.name.replace("(copy)", "").trim()} (copy)"
duplicatedLocation.equipments = duplicateEquipment originalLocation.equipments, duplicatedLocation
duplicatedLocation.products = duplicateProducts originalLocation, duplicatedLocation
duplicatedLocation.save()
duplicatedLocation
}
/**
* Performs a deep copy of a given locations equipments.
*
* @param originalEquipments The original locations equipments
* @param duplicatedLocation The duplicated location; needed for belongsTo association
* @return The duplicated equipment set.
*/
Set<Equipment> duplicateEquipment(Set<Equipment> originalEquipments, Location duplicatedLocation) {
Set<Equipment> duplicatedEquipments = []
for (originalEquipment in originalEquipments) {
Equipment duplicatedEquipment = new Equipment()
duplicatedEquipment.properties = originalEquipment.properties
duplicatedEquipment.uuid = UUIDGenerator.createUniqueId()
duplicatedEquipment.dateModified = DateUtil.currentDate
duplicatedEquipment.location = duplicatedLocation
duplicatedEquipment.extras = null
duplicatedEquipment.save()
duplicatedEquipment.name = "${originalEquipment.name.replace("(copy)", "").trim()} (copy)"
duplicatedEquipment.extras = duplicateExtras originalEquipment.extras, duplicatedEquipment
duplicatedEquipments << duplicatedEquipment
}
duplicatedEquipments
}
/**
* Performs a deep copy of a given locations extras.
*
* @param originalExtras The original location extras
* @param duplicatedEquipment The duplicated equipment; needed for belongsTo association
* @return The duplicated extras set.
*/
Set<EquipmentQuestionExtra> duplicateExtras(Set<EquipmentQuestionExtra> originalExtras, Equipment duplicatedEquipment) {
Set<EquipmentQuestionExtra> duplicatedExtras = []
for (originalExtra in originalExtras) {
EquipmentQuestionExtra duplicatedExtra = new EquipmentQuestionExtra()
duplicatedExtra.properties = originalExtra.properties
duplicatedExtra.equipment = duplicatedEquipment
duplicatedExtra.uuid = UUIDGenerator.createUniqueId()
duplicatedExtra.dateModified = DateUtil.currentDate
duplicatedExtra.save()
duplicatedExtras << duplicatedExtra
}
duplicatedExtras
}
/**
* Performs a deep copy of a given locations products.
*
* @param originalLocation The original location
* @param duplicatedLocation The duplicated location
* @return The duplicated product set.
*/
Set<RecommendedProduct> duplicateProducts(Location originalLocation, Location duplicatedLocation) {
Set<RecommendedProduct> originalProducts = originalLocation.products
Set<RecommendedProduct> duplicatedProducts = []
for (originalProduct in originalProducts) {
RecommendedProduct duplicatedProduct = new RecommendedProduct()
duplicatedProduct.properties = originalProduct.properties
duplicatedProduct.location = duplicatedLocation
duplicatedProduct.uuid = UUIDGenerator.createUniqueId()
duplicatedProduct.dateModified = DateUtil.currentDate
duplicatedProduct.save()
duplicatedProducts << duplicatedProduct
}
duplicatedProducts
}
}