Reputation: 35961
I have two following domains:
User {
UserData userData
}
UserData {
static belongsTo = [user: User]
}
and at some point I want to merge two users into one. I mean delete one instance of User and attache userData to another user.
I've tried:
User zombieUser
User liveUser
UserData data = zombieUser.userData
zombieUser.delete()
liveUser.userData = data
userData.user = liveUser
userData.save()
liveUser.save()
Actually I've tried different variants, different order, seems that all possible ways. But it always fails with an exceptions. Current code will fail with:
deleted object would be re-saved by cascade (remove deleted object from associations): [UserData#1]
If i've put zombie.delete()
to bottom, after *.save()
, I will get:
Field error in object 'User' on field 'userData': rejected value [UserData : 1]; codes ... default message [Property [{0}] of class [{1}] with value [{2}] must be unique]
Are there any way to reconnect existing object from one object to another?
Working code:
UserData userData = zombieUser.userData
userData.user = null
zombieUser.userData = null
zombieUser.save(flush: true)
userData.save()
liveUser.userData = userData
userData.user = liveUser
liveUser.save()
userDate.save(flush: true)
Upvotes: 1
Views: 1348
Reputation: 5198
The problem is that because of your belongsTo
declaration, and according to cascading rules, userData
is deleted from the database as soon as zombieUser
is deleted. When liveUser.save()
is called, userData
would be saved again. By the way, you call to userData.save()
is not needed as again, because of this belongsTo
declaration, liveUser.save()
is cascaded to userData
.
I see two options for solving your problem:
Perform a deep copy of userData
to a new UserData
object, and attach this object to liveUser
, like this:
withTransaction {
UserData data = new UserData(prop: zombieUser.userData.prop)
liveUser.userData = data
zombiUser.delete()
liveUser.save()
}
The old UserData object will be deleted by cascade when deleting zombieUser
and a new one will be created when saving liveUser
.
Remove belongsTo
from userData
. You won't benefit of cascading anymore, and will have to manage saving userData
yourself, and make sure user.userData
is deleted from the database when no user
reference it (don't forget to make transactions for that, as always when you have several call to save
or delete
in your method).
Which approach you choose depends on how coupled your objects are. Typically, if they are very coupled, you will benefit of cascading (through belongsTo
) and may go for 1. If they are not very coupled, (i.e. there may be several users with the same userData), then belongsTo
is wrong and you should choose approach 2.
The concepts presented in GORM gotchas will greatly help you if you have a GORM model with complicated dependencies.
Upvotes: 1
Reputation: 7906
You need to add a mapping to your domain class to tell it what to do when you delete an object. For example, should deleting a parent also delete all its children??
static mapping = {
userData cascade: "all-delete-orphan"
}
Upvotes: 0