JPA: пожалуйста, помогите понять "присоединиться к" - PullRequest
13 голосов
/ 27 февраля 2009

У меня есть следующая структура объекта: Бизнес -> Кампания -> Продвижение, где ОДИН Бизнес может иметь МНОГИЕ Кампании, а ОДНА Кампания может иметь МНОГО Акции. Оба отношения «один ко многим» объявлены как «Ленивые». В одном месте моего кода мне нужно с нетерпением получить обе коллекции из Business, поэтому я делаю:

    Query query = entityManager.createQuery("select b from Business b " +
            "left join fetch b.campaigns c " +
            "left join fetch c.promotions where b.id=:id");
query.setParameter("id", b.getId());
business = (Business) query.getResultList().get(0);

Однако запрос возвращает список результатов, который содержит 4 бизнес-объекта, все 4 объекта ссылаются на один и тот же бизнес-экземпляр. В моей базе данных у этого бизнеса есть 3 кампании, а во всех 3 кампаниях - 3 акции.

У меня есть два вопроса:

  1. Сначала я использую List, чтобы содержать множество сторон отношений, но когда программа запускается, я получаю исключение "org.hibernate.HibernateException: не могу одновременно получить несколько пакетов". Затем я погуглил это исключение, и похоже, что я должен использовать Set, а не List. Поэтому я изменил коллекцию на Set, и это сработало. Может кто-нибудь сказать мне, почему List не будет работать в этой ситуации?

  2. Я ожидаю, что запрос вернет один результат, потому что он запрашивает идентификатор, который является первичным ключом и, следовательно, должен возвращать только один результат. Но оказывается, что он возвращает 4 экземпляра в списке. Это проблема? Или это ожидаемое поведение?

Любая помощь будет принята с благодарностью.

Ответы [ 4 ]

20 голосов
/ 27 февраля 2009

Сгенерированный sql будет выглядеть примерно так:

select * from Business b 
left outer join campaigns c on c.business_id = b.id
left join promotions  p on p.campaign_id = c.id
where b.id=:id

Внутренне Hibernate будет иметь только один бизнес-экземпляр, однако дубликаты будут сохранены в наборе результатов. Это ожидаемое поведение. Требуемое поведение может быть достигнуто либо с помощью предложения DISTINCT, либо с помощью LinkedHashSet для фильтрации результатов:

Collection result = new LinkedHashSet(query.getResultList());

, который будет возвращать только уникальные результаты, сохраняя порядок вставки.

«org.hibernate.HibernateException: не может одновременно получить несколько сумок» возникает всякий раз, когда вы пытаетесь охотно получить более одной коллекции упорядоченным способом (и, возможно, дублировать элементы). Это имеет смысл, если учесть сгенерированный SQL. В Hibernate нет способа узнать, был ли дублированный объект вызван объединением или фактическими дублирующимися данными в дочерней таблице. Посмотрите на это для хорошего объяснения.

3 голосов
/ 27 февраля 2009
  1. Список может иметь избыточные элементы из-за декартового произведения, и люди об этом не знали, поэтому спящие ребята начали выдавать эту ошибку, чтобы заставить людей использовать Set вместо List, чтобы избежать этой проблемы. Источник 1
1 голос
/ 04 сентября 2012

Другой обходной путь вместо использования Set - это использование двух запросов для получения данных: - 1-й для загрузки кампании и связанных с ней рекламных акций бизнеса. Затем загрузите Бизнес и его кампании, используя fetch

    Query query1 = entityManager.createQuery("select c from Business b join b.campaigns c left join fetch c.promotions where b.id=:id");
    query1.setParameter("id", b.getId());
    query1.getResultList();

    Query query2 = entityManager.createQuery("select b from Business b left join fetch       b.campaigns where b.id=:id");
    query2.setParameter("id", b.getId());
    business = (Business) query2.getResultList().get(0);
1 голос
/ 27 февраля 2009
  1. Я предполагаю, что если вы используете List, hibernate предполагает, что порядок важен, и будет выводить порядок элементов из возвращаемых строк, но если вы присоединитесь к другой таблице, hibernate получит каждую кампанию несколько раз что смущает спящий режим. Опять же, я просто предполагаю, что

  2. Это ожидаемое поведение. Используйте RootEntityResultTransformer для получения единственного корневого объекта: http://www.hibernate.org/hib_docs/v3/api/org/hibernate/transform/RootEntityResultTransformer.html

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...