HQL левое соединение несвязанных объектов
У меня есть 2 объекта, A
и B
. Они связаны, но я не хочу добавлять сопоставление отношений в beans.
Как мы можем использовать левое внешнее соединение между A
и B
с использованием HQL или критериев?
Для этого есть некоторые обходные пути,
- Используйте Native SQL, как сказано здесь.
- Добавьте связь и используйте выберите a из A слева. a.b.
- Мы можем сделать внутреннее соединение в HQL как выбрать * из A a, B b, где a.some = b.some
Я всегда возвращал эти 2 варианта, есть ли альтернатива для этого? Или это невозможно?
Ответы
Ответ 1
В настоящее время тета-стиль при соединении несвязанных классов в предложении where с использованием HQL поддерживает только внутреннее соединение.
запрос для поддержки внешнего соединения для такой ситуации в настоящее время является 3-м большинством голосов. но я не думаю, что эта функция будет реализована в ближайшей функции, так как она требует повторной реализации текущего анализатора запросов ANTLER, который, по-видимому, является гигантская задача ИМО.
Если вы настаиваете на том, чтобы использовать HQL для выполнения левого соединения без добавления отношения между A и B, вы можете использовать опцию 3, чтобы сначала выполнить внутреннее соединение, а затем использовать следующий HQL
from A a where a.some not in ( select b.some from B)
чтобы узнать все A, которые не могут присоединиться к B и объединить результаты программно.
Update
Начиная с версии 5.1.0 HHH-16 (Явные объединения для несвязанных классов) фиксирован, и мы должны иметь возможность присоединиться к несвязанным объектам.
Ответ 2
Как сказал Кен Чен, вы не можете сделать это напрямую в одном запросе HQL.
Относительно ваших трех возможностей:
- Исходный SQL: Не рекомендуется. Синтаксис внешних соединений довольно различен между различными базами данных.
- Добавить отношения: Что бы я сделал. Это не дорогого кода или памяти, и это быстро запрограммировано.
- Внутреннее соединение: это не работает (недостающие строки), если отношение действительно является внешним соединением в базе данных.
Если по каким-либо особым причинам вы действительно не хотите добавлять отношения, вы можете разделить запрос на два отдельных запроса и вносить результат вручную в 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-код.
(Решение аналогично решению, предложенному Кеном Чэном, но оно позволяет избежать "не в" и внутреннего выбора, которые оба могут быть неэффективными в базе данных.)
Ответ 3
Если вы знаете, что для каждого A существует максимум 1 B, вы также можете использовать подзапрос.
Например:
select a, (select b from B b where b.some = a.some)
from A a
Если вы знаете, что существует хотя бы 1 B, вы также можете использовать следующий запрос, но он не рекомендуется, так как это взлом:
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))
Ответ 4
Жирный оператор: вы не можете.
Почему? JPQL (и HQL) ожидает путь между объектами, чтобы присоединиться к ним.
То, что я обычно делаю в ситуации, подобной этой (предпочтительный порядок):
- Свяжите объекты.
- Получить данные, необходимые для программной сборки результатов.
- Или один или три из трех вариантов, о которых вы уже упоминали.