Этот вопрос был поднят как новый запрос после всех моих сегодняшних исследований, которые, по моему мнению, являются дефектом - пожалуйста, сначала прочитайте более новый пост .
У меня есть класс домена RootEntity и сущность EntityRelationhip (здесь у меня есть отдельная явная сущность, поскольку я хочу, чтобы отношения имели собственные атрибуты.
Я хочу две коллекции однонаправленных ссылок из RootEntityк EntityRelationship, как это
A --references --> link (+attribs) --referenced by --> B
Код:
abstract class RootEntity {
Long id
String name
LocalDateTime dateCreated
LocalDateTime lastUpdated
Collection<? extends EntityRelationship> entityReferences = []
Collection<? extends EntityRelationship> entityReferencedBy = []
static hasMany = [entityReferences: EntityRelationship, entityReferencedBy: EntityRelationship]
/* doesn't appear to be required - mucks up the test
static mappedBy = [
entityReferences : "references",
entityReferencedBy : "referencedBy"
]*/
static mapping = {
tablePerHierarchy false //multiple tables+joins
}
static constraints = {
entityReferences nullable:true
entityReferencedBy nullable:true
}
}
И у моего объекта EntityRelationship есть два дискретных ForKeys - один для ссылочной (сущность, которой принадлежит «ссылка», и другой referencedBy для сущностейна удаленном конце, как это:
class EntityRelationship<M extends RootEntity, N extends RootEntity> {
String relationshipType
String name
String owningRole
String referencedRole
M references //fk to owning entity
N referencedBy //fk to other end
static mappedBy = [references: "none", referencedBy: "none"] //seems to need this here - but not in RootEntity
static constraints = {
relationshipType unique:true, nullable:true
name nullable:true
owningRole nullable:true
references nullable:true
referencedRole nullable:true
referencedBy nullable:true
}
}
Для того, чтобы мой тест работал правильно - фрагмент теста интеграции выглядит следующим образом
...
EntityRelationship <Device, Device> rel = new EntityRelationship()
rel.name = "i need a PE"
rel.owningRole = "i need this PE "
rel.referencedRole = "i am supporting "
ce.addToEntityReferences(rel)
pe.addToEntityReferencedBy(rel)
rel.save(failOnError:true)
assert ce.entityReferences.size() == 1
assert ce.entityReferencedBy.size() == 0
assert pe.entityReferences.size() == 0
assert pe.entityReferencedBy.size() == 1
Итак, читая примеры, которые у меня быличтобы было закрытие mappedBy со стороны один ко многим (rootEntity) - чтобы указать hibernate, какой столбец в EntityLink должен был быть обновлен.
Однако, если бы я попытался это сделать в тестовом коде - он сказал бы, что pe.referencedBy.size () было 2, а не 1, как и ожидалось, и не удалось. Я посмотрел на ce и pe и rel в отладчике, и ониked прямо в памяти - но утверждение для pe.referencedBy терпит неудачу.
Чтобы исправить это, похоже, закомментируйте mappedBy в RootEntity, который определяет коллекции «один ко многим», ивместо этого поместите предложение mappedBy в таблицу many (EntityLink) с отображенным полем: нет.
Теперь, когда я запускаю тест, это будет работать.
Это выглядит как "против" того, что в примере gormпредлагает (пример полетов и аэропортов), где говорится, что mappedBy должен быть на объекте полетов.
Так что я ясно не понял тонкости mappedBy.Почему мой рабочий код должен иметь mappedBy на многих сторонах?
Исправление и дополнения
На самом деле это страннее, чем я думал
PS: медленно прошел черезотладчик и мне нужно mappedBy на RootEntity.Это гарантирует, что вызов addToReferences () и addToReferencedBy () правильно обновит правильный атрибут в EntityRelationship - исправленная форма теперь выглядит следующим образом:
RootEntity.groovy
abstract class RootEntity {
//id provided default by grails implicit in infrastructure - shown explicitly here
Long id
String name
LocalDateTime dateCreated
LocalDateTime lastUpdated
Collection<? extends EntityRelationship> entityReferences = []
Collection<? extends EntityRelationship> entityReferencedBy = []
static hasMany = [entityReferences: EntityRelationship, entityReferencedBy: EntityRelationship]
static mappedBy = [
entityReferences : "references", //map entityReferences to EntityRelationship.references
entityReferencedBy : "referencedBy" //map entityReferencedBy to EntityRelationship.referencedBy
]
static mapping = {
tablePerHierarchy false //multiple tables+joins
}
static constraints = {
entityReferences nullable:true
entityReferencedBy nullable:true
}
}
EntityRelationship.groovy выглядит следующим образомthis
class EntityRelationship<M extends RootEntity, N extends RootEntity> {
String relationshipType
String name
String owningRole
String referencedRole
M references //fk to owning entity
N referencedBy //fk to other end
static mappedBy = [references: "none", referencedBy: "none"] //seems to need this here - but not in RootEntity
static constraints = {
relationshipType unique:true, nullable:true
name nullable:true
owningRole nullable:true
references nullable:true
referencedRole nullable:true
referencedBy nullable:true
}
static EntityRelationship createRelationship(String name, M from, N to) {
if (from == null || to == null)
return null
EntityRelationship rel = new EntityRelationship()
rel.name = name
rel.owningRole = from.name
rel.referencedRole = to.name
from.addToEntityReferences (from)
to.addToEntityReferencedBy (to)
rel.save(failOnError: true)
rel
}
}
Однако это работает не так, как ожидалось.Это странный бит.
Я установил две точки останова, одну непосредственно перед rel.save (), а другую сразу после нее.
Раздел моего интеграционного теста:
...EntityRelationship <Device, Device> rel = new EntityRelationship()
rel.name = "i need a PE"
rel.owningRole = "i need this PE "
rel.referencedRole = "i am supporting "
ce.addToEntityReferences(rel)
pe.addToEntityReferencedBy(rel)
rel.save(failOnError:true) //set Breakpoint 1
assert ce.entityReferences.size() == 1 //set breakpoint 2
assert ce.entityReferencedBy.size() == 0
assert pe.entityReferences.size() == 0
assert pe.entityReferencedBy.size() == 1
Если я запускаютест без отладки - тест завершается неудачно с pe.entityReferencedBy.size (), возвращаемым как 2.
Если я запускаю отладчик и сразу же перехожу через первую точку останова на вторую, то смотрю на pe.entityReferencedByколлекция, в которой есть два элемента, утверждается, а затем завершается неудачей.
Однако если я остановлюсь на первой точке останова и осмотрю коллекцию pe.entityReferencedBy до rel.save (), то в коллекции referencedBy будет одна запись.Вы проходите через сохранение и проверяете снова - все в порядке.Когда тест завершен, тест работает!
Таким образом, он работает без отладки или проверки после rel.save (), тогда ответ неверный.
Если я остановлюсь и проверю pe.entityReferencedBy перед вызовом rel.save (), ответ верен только 1 запись.
Почему это происходит, и, что более важно, почему он вообще не работаетбез отладчика?
Addenda 2
Мой последний ход сегодня вечером - заменил вызовы addToMethod и настроил вручную, как это
//ce.addToEntityReferences(rel)
//pe.addToEntityReferencedBy(rel)
ce.entityReferences << rel
rel.references = ce
//pe.entityReferencedBy << rel
rel.referencedBy = pe
rel.save(failOnError:true)
Так что, если я открою pe.entityReferencedBy << rel и запустите сохранение - я, кажется, получаю две записи в коллекции referencedBy на PE.Если я добавлю rel к экземпляру ce, установите rel.rel.referencedBy = pe, но исключим добавление в коллекцию entityReferencedBy pe - тест говорит, что он работает. </p>
Теперь я в замешательстве, когда запускаю действие rel.save (), которое, по-видимому, выполняет автоматическую вставку обратно в коллекцию pe.entityReferenced (но не выполняет дополнительного обновления обратно в ce.