Пропустить дочерний элемент для извлечения родителя - JPA

Я столкнулся с проблемой, когда данные рекурсивно загружаются. Я хотел, чтобы ребенок не извлекал родительские данные. Что вызывает рекурсивную проблему. Я упомянул код ниже

Структура Пожо

class Parent  {
    ..
    @OneToMany(mappedBy = "parent", fetch = FetchType.LAZY)
    private List<Child> childs;

    ..
    }

class Child {
    ..
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "parentId")
    private Parent parent;
    ..
    }

Получение таких данных

 `  em = EMF.get().createEntityManager();
    Query q = em.createQuery("Select p from Parent p", Parent.class);
    List<Parent> parents = q.getResultList();
    // Till this point all looks good but when the code gets executed 
    parent.getChilds();
`

Он извлекает данные следующим образом:

Parent
child1
    Parent
        child2
            Parent
                child2
                    Parent
                ..
        ..
child2
..

Что мне не нужно, мне просто нужны такие данные:

Parent1
    child1
    child2
Parent2
    child1
    child2
    child3

person Community    schedule 24.05.2017    source источник
comment
По умолчанию режим выборки по умолчанию ленив.   -  person soorapadman    schedule 24.05.2017
comment
@soorapadman по умолчанию выборка ленива для многих и стремится к отношениям с одним   -  person crizzis    schedule 24.05.2017
comment
Какой провайдер? Ленивая выборка OneToOne/ManyToOne зависит от поставщика и может потребовать чего-то большего в зависимости от вашей среды. EclipseLink, например, требует переплетения eclipse.org/eclipselink/documentation/2.5/concepts /   -  person Chris    schedule 24.05.2017


Ответы (4)


В то время как FetchType.EAGER — это контракт, FetchType.LAZY — это только подсказка, потому что ленивая выборка не всегда возможна. Это может зависеть, например. на поставщике JPA, который вы используете, а также на его конфигурации. Ленивая выборка особенно проблематична в отношениях с одним человеком.

Если у каждого Child есть Parent, попробуйте добавить optional=false к вашему @ManyToOne. Это может включить ленивую выборку.

Поскольку сущность Parent уже загружена в контекст постоянства, заполнение Children.parent не должно инициировать запросы к базе данных. Вы действительно видите выполнение запросов? Откуда вы знаете, что Children.parent загружается? Если вы обращаетесь к значению, чтобы проверить этот факт, скорее всего, вы сами запускаете загрузку по требованию.

person crizzis    schedule 24.05.2017
comment
Если у каждого дочернего элемента есть родитель, попробуйте добавить option=false к вашему @ManyToOne. Это может включить ленивую выборку. Это не сработало. Откуда вы знаете, что Children.parent загружается? Мне нужно выполнить parent.getChilds(); и когда я это делаю, я вижу в нем родительский объект, а в этом родительском объекте есть дочерний объект и так далее. вы можете получить представление, просмотрев: drive.google.com/file /d/0B_A6oOU92Jt8M2pGZlpaQ3RuQTA/ - person ; 24.05.2017
comment
ХОРОШО. Как я уже сказал, это не проблема производительности, потому что объект Parent уже присутствует в контексте персистентности, поэтому поиск дочерних элементов Parent не попадает в базу данных. Тем не менее, если вам нужна ленивая загрузка, используете ли вы JPA в Java SE или Java EE? Чтобы отложенная загрузка работала с отношениями «к одному», необходимо включить переплетение (см.: eclipse.org/eclipselink/documentation/2.6/concepts/ и абзац непосредственно над ним) - person crizzis; 24.05.2017
comment
у нас есть Java EE и EclipseLink, версия: Eclipse Persistence Services — 2.0.2.v20100323-r6872. Да, это не производительность, но приложение зависает. Причина в том, что это двунаправленная связь, когда мы извлекаем дочерний элемент, он также извлекает родителя, что снова устанавливает дочерний элемент с родителем внутри него. Это приводит к зависанию сервера из-за циклических значений. - person ; 25.05.2017
comment
Я не уверен, что EclipseLink сам по себе может вызвать здесь бесконечный цикл, поскольку это было бы серьезной ошибкой для довольно распространенного варианта использования. Вы уверены, что parent.getChilds() является источником проблемы? Видите ли вы другие методы в трассировке стека (я так понимаю, вы в конечном итоге получаете StackOverflowError)? Тот факт, что вам удалось проверить значение Parent.getChilds() с помощью отладчика, говорит о том, что загрузка дочерних элементов завершена успешно, а ошибка вызвана каким-то другим кодом, выполненным позже... - person crizzis; 25.05.2017
comment
Нет ни StackOverflowError, ни любого другого исключения. Проблема в том, что это занимает более 1 минуты, что приводит к тайм-ауту нашего сервера. Нам нужно получить результат за одну минуту. Если мы увеличим время ожидания сервера, он будет работать нормально. - person ; 25.05.2017

Это будет работать как бесконечный цикл для больших данных. Лучше всего упоминать @JsonIgnore в столбце дочернего класса. Поблагодаришь меня позже

person Divvy Vyas    schedule 19.04.2020

Чтобы избежать этой проблемы, пожалуйста, объявите ниже методы получения аннотаций Parent & Child.

   @JsonBackReference
    public Parent getParent() {
        return parent;
    }

@JsonManagedReference
    public List<Child> getChilds() {
    return childs;
    }
person Rajhesh Kumar    schedule 15.06.2020
comment
Возможно, вы захотите немного подробнее описать, почему это решает проблему в вопросе, а не просто вставляет некоторый код. :) - person Qben; 15.06.2020

Чтобы избежать этой проблемы циклических ссылок, я использовал Mapper с MapStruct (см. официальная документация для быстрой настройки):

Затем я могу легко написать картограф, чтобы игнорировать такое свойство:

public interface SecondaryObjectMapper {
  @Mapping(target = "primaryObject.secondaries", ignore=true),
  SecondaryObjectDto toSecondaryObjectDto(SecondaryObject source);
}

и используйте его, например, так:

repository.findAll(pageable).map(secondaryObjectMapper::toSecondaryObjectDto)
person DependencyHell    schedule 21.12.2020