Arhn - архитектура программирования

Почему TypedQuery.getResultList () разрешает каждую связь ManyToOne с отдельным SELECT?

Учитывая следующую простую ассоциацию сущностей: (EntityA) * -1 (EntityB), созданную в базе данных с внешним ключом в EntityA (entityB_id).

Объекты JPA отображают эту взаимосвязь однонаправленно:

@Entity
EntityA {
    @Id
    @GeneratedValue
    private long id;

    @Column(nullable=false,length=250)
    private String name;

    @ManyToOne(optional=false)
    private EntityB entityB;

    ... getter/setter ...
}

@Entity
EntityB {
    @Id
    @GeneratedValue
    private long id;

    @Column(nullable=false,length=250)
    private String name;

    ... getter/setter ...
}

Если сделать простой запрос:

EntityManager em = ...;
TypedQuery<EntityA> tq = em.createQuery("from EntityA a", EntityA.class);
tq.getResultList();

В выводе отладки SQL Hibernate я вижу, что запрос EntityB выполняется для каждой строки EntityA:

Hibernate: 
    select
        entitya0_.id as id8_,
        entitya0_.entityB_id as entityB3_8_,
        entitya0_.name as name8_ 
    from
        EntityA entitya0_
Hibernate: 
    select
        entityb0_.id as id4_0_,
        entityb0_.name as name4_0_ 
    from
        EntityB entityb0_ 
    where
        entityb0_.id=?

Даже если по умолчанию используется стратегия выборки EAGER (что, похоже, так), EntityB следует извлекать через implizit join, не так ли? Что не так?

Но это становится еще более странным - если загружен только один объект EntityA:

EntityA a = em.find(EntityA.class, new Long(1));

Тогда Hibernate, кажется, понимает задачу:

Hibernate: 
    select
        entitya0_.id as id1_1_,
        entitya0_.entityB_id as entityB3_1_1_,
        entitya0_.name as name1_1_,
        entityb1_.id as id12_0_,
        entityb1_.name as name12_0_ 
    from
        EntityA entitya0_ 
    inner join
        EntityB entityb1_ 
        on entitya0_.entityB_id=entityb1_.id 
    where
        entitya0_.id=?

Вышеупомянутые тесты были выполнены с Hibernate 3.5 и JPA 2.0.

11.11.2010

Ответы:


1

Даже если по умолчанию используется стратегия выборки EAGER (что, похоже, так), EntityB следует извлекать через неявное соединение, не так ли? Что не так?

Действительно, значение по умолчанию FetchType для ManyToOne равно EAGER. Но это просто говорит о том, что сторона One должна загружаться когда сторона Many загружается, а не как. Как оставить на усмотрение поставщика персистентности (а JPA не позволяет настраивать стратегию).

В Hibernate есть особые Fetch аннотации, позволяющие настроить режим выборки. Из документации:

2.4.5.1. Ленивые параметры и режимы загрузки

JPA поставляется с опцией выборки для определения режимов отложенной загрузки и выборки, однако Hibernate имеет гораздо больше опций, установленных в этой области. Для точной настройки стратегий отложенной загрузки и выборки были введены некоторые дополнительные аннотации:

  • [...]

  • @Fetch: определяет стратегию выборки, используемую для загрузки ассоциации. FetchMode может быть SELECT (выбор запускается, когда необходимо загрузить ассоциацию), SUBSELECT (доступно только для коллекций, использовать стратегию частичного выбора - дополнительную информацию см. В справочной документации Hibernate) или JOIN (использовать SQL JOIN для загрузки ассоциация при загрузке объекта-владельца). JOIN переопределяет любой ленивый атрибут (ассоциация, загруженная с помощью стратегии JOIN, не может быть ленивой).

Вы можете попробовать следующее (если вы не против использования аннотаций для конкретных поставщиков):

@ManyToOne(optional=false)
@Fetch(FetchMode.JOIN)
private EntityB entityB;
11.11.2010
  • Спасибо, Паскаль. Это именно то, что я искал. Я попробую завтра. 12.11.2010
  • Извините за это, Паскаль, но я получаю те же результаты независимо от того, используется эта аннотация или нет. 12.11.2010
  • Кстати, я могу получить желаемое поведение с запросом «выберите a из EntityA, а левое соединение выберите a.entityB». Но мне интересно, почему hibernate не делает этого по умолчанию (или если это указано в аннотациях). Любые идеи? 12.11.2010
  • @Mulmoth: Не нужно извиняться, я просто указываю, что FetchType.EAGER + FetchMode.JOIN предполагается для работы в соответствии с документацией и ветками форума, такими как этот. Но это не исключает каких-либо ошибок, и я не несу ответственности :) Похоже, вы не единственный на самом деле. Проверьте Jira на наличие существующих проблем и, возможно, отправьте их, если потребуется. 12.11.2010
  • Большое спасибо за ваши усилия, Паскаль. Это мой первый проект с JPA / Hibernate, и мне все еще интересно, не понял ли я что-то или сделал ошибку с моими аннотациями. Меня действительно удивляет, что для этого стандартного случая нет интуитивно понятного решения. 12.11.2010
  • @Mulmoth: Я могу это понять, это очень беспокоит, когда что-то работает не так, как рекламируется, независимо от того, имеете ли вы опыт работы с инструментом или нет (мне пришлось дважды проверить, не ошибся ли я с моим предложением и Я думаю, что нет). Подводя итог, можно сказать, что ваши допросы совершенно законны, как ИМО. 12.11.2010

  • 2

    Решение, которое сработало для текущего варианта использования, - включить в оператор соединение выборки:

    select a from entityA left join fetch a.entityB
    

    Это извлечет все связанные EntityB (и переопределит FetchType.LAZY).

    16.11.2010

    3

    Во-первых, почему вы вообще об этом беспокоитесь? Смысл использования библиотеки ORM в том, чтобы вам не приходилось иметь дело с такими деталями. Если вы видите, что какая-то операция выполняется медленно, тогда вы начинаете думать об оптимизации запросов.

    Я думаю, что стратегия Hibernate имеет смысл, поскольку это отношение ManyToOne. Это означает, что количество EntityB, возвращаемых в запросе, никогда не будет больше, чем количество EntityAs, и обычно будет меньше. Поэтому попытка получить данные из EntityB через соединение, вероятно, будет означать отправку избыточных копий одних и тех же данных.

    11.11.2010
  • Спасибо, Майк за ответ. Я новичок в Hibernate / JPA, поэтому я - при условии, что это должно быть сделано более эффективным способом - задаюсь вопросом, не ошибся ли я с аннотациями. В реальном проекте EntityA имеет три ассоциации. Визуализация запроса, который возвращает 100 EntityAs - с описанным поведением он будет извлекать 400 объектов (с 301 SQL-выражением), вероятно, без необходимости в этом. 11.11.2010
  • Если вам не нужны связанные сущности, вы должны пометить ассоциацию как ленивую. И вы знаете, что они говорят о предположении. 11.11.2010
  • Запросы выполняются, даже если ассоциация отмечена как ленивая. Более того, мне нужны связанные объекты. 12.11.2010
  • Извините, это было неправильно - запросы не выполняются с помощью FetchType.LAZY. Но, как я уже упоминал, мне нужно, чтобы связанные объекты были извлечены эффективным способом. 12.11.2010
  • Новые материалы

    Коллекции публикаций по глубокому обучению
    Последние пару месяцев я создавал коллекции последних академических публикаций по различным подполям глубокого обучения в моем блоге https://amundtveit.com - эта публикация дает обзор 25..

    Представляем: Pepita
    Фреймворк JavaScript с открытым исходным кодом Я знаю, что недостатка в фреймворках JavaScript нет. Но я просто не мог остановиться. Я хотел написать что-то сам, со своими собственными..

    Советы по коду Laravel #2
    1-) Найти // You can specify the columns you need // in when you use the find method on a model User::find(‘id’, [‘email’,’name’]); // You can increment or decrement // a field in..

    Работа с временными рядами спутниковых изображений, часть 3 (аналитика данных)
    Анализ временных рядов спутниковых изображений для данных наблюдений за большой Землей (arXiv) Автор: Рольф Симоэс , Жильберто Камара , Жильберто Кейрос , Фелипе Соуза , Педро Р. Андраде ,..

    3 способа решить квадратное уравнение (3-й мой любимый) -
    1. Методом факторизации — 2. Используя квадратичную формулу — 3. Заполнив квадрат — Давайте поймем это, решив это простое уравнение: Мы пытаемся сделать LHS,..

    Создание VR-миров с A-Frame
    Виртуальная реальность (и дополненная реальность) стали главными модными терминами в образовательных технологиях. С недорогими VR-гарнитурами, такими как Google Cardboard , и использованием..

    Демистификация рекурсии
    КОДЕКС Демистификация рекурсии Упрощенная концепция ошеломляющей О чем весь этот шум? Рекурсия, кажется, единственная тема, от которой у каждого начинающего студента-информатика..