Grails заменяют объект в взаимно-однозначной ассоциации - PullRequest
1 голос
/ 22 ноября 2011

У меня есть два следующих домена:

User {
   UserData userData
}

UserData {
   static belongsTo = [user: User]
}

и в какой-то момент я хочу объединить двух пользователей в один.Я имею в виду удалить один экземпляр User и прикрепить userData к другому пользователю.

Я пробовал:

User zombieUser
User liveUser

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

На самом деле я пробовал разные варианты, в другом порядке, кажется, что все возможные способы,Но это всегда терпит неудачу за исключением.Текущий код не будет работать с:

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

Если я поставил zombie.delete() на дно, после *.save() я получу:

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]

Есть ли способ переподключиться?существующий объект от одного объекта к другому?


Рабочий код:

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)

Ответы [ 2 ]

1 голос
/ 22 ноября 2011

Проблема в том, что из-за вашей декларации belongsTo и согласно каскадным правилам , userData удаляется из базы данных, как только zombieUser удаляется.Когда вызывается liveUser.save(), userData будет сохранен снова.Кстати, вам звонить на userData.save() не нужно, так как снова, из-за этого объявления belongsTo, liveUser.save() каскадно к userData.

Я вижу два варианта для решения вашей проблемы:

  1. Выполните глубокую копию userData для нового UserData объекта и прикрепите этот объект к liveUser, вот так:

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

    Старый объект UserData будет удален каскадно при удалении zombieUser, а новый будет создан при сохранении liveUser.

  2. Удалите belongsTo из userData. Вам больше не понадобится каскадирование, вам придется самостоятельно управлять сохранением userData и убедиться, что user.userData удалено из базы данных, когда нет userсослаться на него (не забывайте совершать для этого транзакции, как всегда, когда в вашем методе есть несколько вызовов save или delete).

Какой подход вы выберете, зависито том, как связаны ваши объекты.Как правило, если они очень связаны, вы получите выгоду от каскадирования (через belongsTo) и можете перейти к 1. Если они не очень связаны (то есть может быть несколько пользователей с одинаковыми userData), тогда belongsToневерно, и вы должны выбрать подход 2.

Концепции, представленные в GORM GOTCHAS , очень помогут вам, если у вас есть модель GORM со сложными зависимостями.

0 голосов
/ 22 ноября 2011

Вам необходимо добавить сопоставление в свой класс домена, чтобы указать, что делать при удалении объекта. Например, следует ли удалить родителя и удалить всех его потомков ??

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