Однонаправленный один ко многим и отображается в Grails - не может пройти тест интеграции - PullRequest
0 голосов
/ 30 января 2019

Этот вопрос был поднят как новый запрос после всех моих сегодняшних исследований, которые, по моему мнению, являются дефектом - пожалуйста, сначала прочитайте более новый пост .

У меня есть класс домена 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.

...