2012-03-27 2 views
41

У меня есть 2 объекта, A и B. Они связаны друг с другом, но я не хочу добавлять сопоставление отношений к компонентам.HQL левое объединение несвязанных объектов

Как мы можем использовать левое внешнее соединение между A и B с использованием HQL или критериев?

Есть некоторые обходные пути, доступные для этого,

  1. Использование Native SQL, как сказал here.
  2. Добавить связь и использовать выберите a из A a join join a.b.
  3. Мы можем сделать внутреннее соединение в HQL, как выберите * из А, В б, где a.some = b.some

Я всегда идет назад эти 2 варианта, есть любая альтернатива для этого? Или это невозможно?

ответ

51

В настоящее время тета-стиль при соединении несвязанных классов в предложении where с использованием HQL поддерживает только внутреннее соединение.

request для поддержки внешнего соединения для такой ситуации currently the 3-rd most voted enhancement, но я не думаю, что эта функция будет реализована в ближайшей функции, как это требует re-implementation of the current ANTLER-based query parser первого который, кажется гигантской задача ИМО.

Если вы настаиваете, чтобы использовать HQL выполнить левое соединение без добавления отношения между А и В, можно использовать вариант 3, чтобы сделать внутреннее соединение, а затем с помощью следующей HQL

from A a where a.some not in (select b.some from B) 

найти из всех A, которые не могут присоединиться к B и объединить результаты программно.

Update

Начиная с версии 5.1.0 HHH-16 (Explicit joins on unrelated classes) фиксируется, и мы должны быть в состоянии присоединиться к несвязанные сущности.

+1

Кристалл острым. Благодаря! –

+0

Не могли бы вы указать на источник синтаксиса новой опции «join on»? Я попробовал «SELECT a FROM A a LEFT JOIN B b ON a.x = b.y' и все еще получил« org.hibernate.hql.internal.ast.QuerySyntaxException: путь, ожидаемый для соединения! ». (Я добавил 'hibernate-core' версию 5.1.0.Final к моему файлу maven pom, другие артефакты, которые я буду в более старых версиях. Но когда я прочитал [HHH-16] (https://hibernate.atlassian.net/projects/ HHH/issues/HHH-16? Filter = allissues & orderby = votes + DESC% 2C + priority + DESC% 2C + updated + DESC), этого должно быть достаточно.) – Joe7

+0

Спасибо за обновление и указатель на закрытую проблему. –

0

Смелое заявление: вы не можете.

Почему? JPQL (и HQL) ожидает пути между объектами, чтобы присоединиться к ним.

То, что я обычно делаю в такой ситуации, как этот один из предпочтительных (порядка):

  1. Свяжите образования.
  2. Извлеките данные, необходимые для программной обработки результатов.
  3. Либо 1 или 3 из трех вариантов, которые вы уже упоминали.
+2

Что нового в этом ответе? Я уже упоминал эти варианты в вопросе, и я не хочу их использовать. ** ожидает, что путь между объектами для их присоединения ** неверен, вы можете сделать внутреннее соединение, даже если нет связи. – ManuPK

+0

Приношу свои извинения. Вот почему я сказал, что это смелое заявление. Просто для уточнения. Если вы имеете в виду перекрестное соединение с условиями (например, использование двух корней), чтобы достичь того же, что и внутреннее соединение, то да, это допустимо. Фактически присоединение (Root.join (...)) - это другой зверь, и это то, что вам нужно, или эквивалент HQL, для достижения вашей цели. До сих пор я не нашел решения этой проблемы. Таким образом, мой ответ: вы не можете. Заметьте, что мне хотелось бы поправляться здесь! –

3

Как сказал Кен Чан, вы не можете сделать это напрямую в одном запросе HQL.

Относительно ваши трех возможностей:

  1. Native SQL: Не рекомендуется. Синтаксис внешних соединений довольно различен между различными базами данных.
  2. Добавить отношения: Это то, что я хотел бы сделать. Это не дорогого кода или памяти, и это быстро запрограммировано.
  3. Внутреннее соединение: это не работает (недостающие строки), если отношение действительно является внешним соединением в базе данных.

Если какими-то особыми причинами вы действительно не хотите, чтобы добавить отношение, вы можете разбить запрос на два отдельных запросов и присоединиться результат вручную в Java, например, как это:

Query qa = session.createQuery("from A a"); 
List la = qa.list(); 

Query qb = session.createQuery("select distinct b.* from B b, A a where a.some=b.some"); 
List lb = qb.list(); 

Map bMap = new HashMap(); 
for (B b : lb) { 
    bMap.put(b.getId(), b); 
} 

/* example with for loop */ 
for (A a : la) { 
    B b = bMap.get(a.getForeignKeyForB()); 
    /* now you have A a and the outer joined B b and you can do with them what you want */ 
    ... 
} 

Это решение имеет (почти) ту же стоимость во время выполнения и памяти как внешнее соединение в базе данных (решение 2.). Это всего лишь несколько java-код.

(Решение аналогично эта предложенный Кен Чан, но он избегает «не» и внутреннего выбора, который и может быть неэффективным в базе данных.)

1

Если вы знаете, что для каждого A есть максимум 1 B, вы также можете использовать подзапрос.

Например:

select a, (select b from B b where b.some = a.some) 
from A a 

Если вы знаете, что существует, по крайней мере, 1 В, вы можете также использовать следующий запрос, но это не рекомендуется, так как это хак:

select a, (select b4 from B b4 where b4=b and b4.some=a.some) from A a, B b 
where a.some=b.some or (a.some not in (select b2.some from B b2) 
and b.id = (select min(b3.id) from B b3)) 
+0

Wow большое вам спасибо ... это решило мою проблему после поиска в течение 3 дней! – kaiser

Смежные вопросы