2011-11-22 5 views
1

J'ai deux domaines suivants:Grails remplacer objet dans un à une association

User { 
    UserData userData 
} 

UserData { 
    static belongsTo = [user: User] 
} 

et à un moment donné, je veux fusionner deux utilisateurs en un seul. Je veux dire supprimer une instance de User et attacher des UserData à un autre utilisateur.

J'ai essayé:

User zombieUser 
User liveUser 

UserData data = zombieUser.userData 
zombieUser.delete() 
liveUser.userData = data 
userData.user = liveUser 
userData.save() 
liveUser.save() 

En fait, je l'ai essayé différentes variantes, un ordre différent, semble que tous les moyens possibles. Mais il échoue toujours avec une exception. Code actuel échouera avec:

deleted object would be re-saved by cascade (remove deleted object from associations): [UserData#1] 

Si j'ai mis zombie.delete() en bas, après *.save(), je vais obtenir:

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] 

Existe-t-il un moyen de reconnecter objet existant d'un objet à un autre?


Code de travail:

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) 

Répondre

1

Le problème est qu'en raison de votre déclaration belongsTo, et selon cascading rules, userData est supprimé de la base de données dès que zombieUser est supprimé. Lorsque liveUser.save() est appelé, userData sera enregistré à nouveau. Par ailleurs, vous appelez userData.save() n'est pas nécessaire, car à cause de cette déclaration belongsTo, liveUser.save() est cascadé à userData.

Je vois deux possibilités pour résoudre votre problème:

  1. Effectuer une copie profonde de userData à un nouvel objet UserData, et attacher cet objet à liveUser, comme ceci:

    withTransaction { 
        UserData data = new UserData(prop: zombieUser.userData.prop) 
        liveUser.userData = data 
        zombiUser.delete() 
        liveUser.save() 
    } 
    

    L'ancien objet UserData sera supprimé en cascade lors de la suppression de zombieUser et un nouveau sera créé w En économisant liveUser.

  2. Supprimer belongsTo de userData. Vous ne bénéficierez plus de la gestion en cascade, et devrez gérer la sauvegarde userData vous même, et assurez-vous que user.userData est supprimé de la base de données quand aucun user n'y fait référence (n'oubliez pas de faire des transactions pour cela, comme toujours lorsque vous avez plusieurs appelez au save ou delete dans votre méthode).

L'approche que vous choisissez dépend de la manière dont vos objets sont couplés.Généralement, s'ils sont très couplés, vous bénéficierez de la cascade (via belongsTo) et peut aller jusqu'à 1. Si ils ne sont pas très couplés, (ie il peut y avoir plusieurs utilisateurs avec la même userData), alors belongsTo est faux et vous devriez choisissez l'approche 2.

Les concepts présentés dans GORM gotchas vous aideront grandement si vous avez un modèle GORM avec des dépendances compliquées.

+0

Merci! Après votre réponse, j'ai trouvé la troisième option: mettre 'userData.user' et' zombie.userData' à 'null' avant la suppression de l'utilisateur zombie. –

0

Vous devez ajouter un mappage à votre classe de domaine pour lui dire ce qu'il faut faire lorsque vous supprimez un objet. Par exemple, la suppression d'un parent doit-elle également supprimer tous ses enfants?

static mapping = { 
    userData cascade: "all-delete-orphan" 
} 
+0

hm, mais que se passe-t-il si vous n'avez pas besoin de supprimer l'objet enfant (UserData)? seul parent (utilisateur). Et attachez l'enfant existant à un autre parent. C'est possible? –

Questions connexes