Какие альтернативы у меня есть для реализации запроса на объединение с использованием спящего режима? Я знаю, что спящий режим не поддерживает запросы на объединение в настоящий момент, сейчас единственный способ, которым я вижу объединение, - это использовать таблицу просмотра.
Другой вариант - использовать простой jdbc, но в этом случае я бы потерял все свои плюсы запросов примеров / критериев, а также проверку сопоставления гибернации, которую спящий режим выполняет для таблиц / столбцов.




Используйте ПРОСМОТР. Одни и те же классы могут быть сопоставлены с разными таблицами / представлениями с использованием имени объекта, поэтому у вас даже не будет большого дублирования. Находясь там, сделал это, работает нормально.
У простого JDBC есть еще одна скрытая проблема: он не знает о кеше сеанса Hibernate, поэтому, если что-то было кэшировано до конца транзакции и не было сброшено из сеанса Hibernate, запрос JDBC не найдет его. Иногда это может быть очень загадочным.
И еще больше озадачивает, когда у вас есть кеш второго уровня, но вы используете простой JDBC (скажем, для отчетности) из какой-то другой части приложения.
Я должен согласиться с Владимиром. Я тоже изучал использование UNION в HQL и не мог найти способ обойти это. Странно то, что я смог найти (в FAQ по Hibernate), что UNION не поддерживается, отчеты об ошибках, относящиеся к UNION, помечены как «исправлено», группы новостей, в которых люди говорят, что утверждения будут усечены в UNION, и другие группы новостей людей, сообщающих, что это работает. отлично... После целого дня возни с ним, я закончил портирование моего HQL обратно на простой SQL, но сделать это в представлении в базе данных было бы хорошим вариантом. В моем случае части запроса генерировались динамически, поэтому мне пришлось вместо этого встроить SQL в код.
Спасибо, что перечислили различные теории, которые вы нашли, и какие из них не верны. Различия между тем, что «разумный разработчик» может ожидать, и тем, что от этого отличается, являются одними из наиболее полезных примечаний.
Представление - лучший подход, но поскольку hql обычно возвращает список или набор ... вы можете сделать list_1.addAll (list_2). Полностью отстой по сравнению с профсоюзом, но должен работать.
Что насчет возврата другого типа? поэтому один список типа A и второй список типа B, но с одинаковыми свойствами (например, таблица A для данных в реальном времени и таблица B для исторических данных)
Я тоже пережил эту боль - если запрос генерируется динамически (например, Hibernate Criteria), я не мог найти практического способа сделать это.
Хорошей новостью для меня было то, что я исследовал union только для решения проблемы с производительностью при использовании «или» в базе данных Oracle.
Решение, опубликованное Патриком (объединение результатов программным способом с использованием набора), в то время как уродливое (тем более, что я также хотел выполнить разбиение на страницы), было для меня адекватным.
Возможно, мне нужно было решить более прямую проблему. Мой «например» был в JPA с Hibernate в качестве провайдера JPA.
Я разделил три выбора (два во втором случае) на множественный выбор и объединил коллекции, возвращенные самим собой, фактически заменив «объединение всех».
Вы можете использовать id in (select id from ...) or id in (select id from ...)
например вместо нерабочий
from Person p where p.name = "Joe"
union
from Person p join p.children c where c.name = "Joe"
ты мог бы сделать
from Person p
where p.id in (select p1.id from Person p1 where p1.name = "Joe")
or p.id in (select p2.id from Person p2 join p2.children c where c.name = "Joe");
По крайней мере, используя MySQL, вы столкнетесь с проблемами производительности позже. Иногда проще выполнить соединение бедняка по двум запросам:
// use set for uniqueness
Set<Person> people = new HashSet<Person>((List<Person>) query1.list());
people.addAll((List<Person>) query2.list());
return new ArrayList<Person>(people);
Часто лучше выполнить два простых запроса, чем один сложный.
Обновлено:
в качестве примера, вот результат EXPLAIN результирующего запроса MySQL из решения подзапроса:
mysql> explain
select p.* from PERSON p
where p.id in (select p1.id from PERSON p1 where p1.name = "Joe")
or p.id in (select p2.id from PERSON p2
join CHILDREN c on p2.id = c.parent where c.name = "Joe") \G
*************************** 1. row ***************************
id: 1
select_type: PRIMARY
table: a
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 247554
Extra: Using where
*************************** 2. row ***************************
id: 3
select_type: DEPENDENT SUBQUERY
table: NULL
type: NULL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: NULL
Extra: Impossible WHERE noticed after reading const tables
*************************** 3. row ***************************
id: 2
select_type: DEPENDENT SUBQUERY
table: a1
type: unique_subquery
possible_keys: PRIMARY,name,sortname
key: PRIMARY
key_len: 4
ref: func
rows: 1
Extra: Using where
3 rows in set (0.00 sec)
Самое главное, 1. row не использует никакого индекса и рассматривает 200k + строк. Плохой! Выполнение этого запроса заняло 0,7 секунды, если оба подзапроса находятся в миллисекундах.
Если вам нужна разбивка на страницы, вы можете использовать два подзапроса, не так ли?
+1 от меня. Хотя часто лучше выполнить два простых запроса, чем один сложный, чаще лучше выполнить один запрос, который использует правильные индексы.
У меня есть решение для одного критического сценария (для которого я много боролся) с объединением в HQL.
например Вместо того, чтобы не работать: -
select i , j from A a , (select i , j from B union select i , j from C) d where a.i = d.i
ИЛИ ЖЕ
select i , j from A a JOIN (select i , j from B union select i , j from C) d on a.i = d.i
ВЫ могли бы сделать в Hibernate HQL ->
Query q1 =session.createQuery(select i , j from A a JOIN B b on a.i = b.i)
List l1 = q1.list();
Query q2 = session.createQuery(select i , j from A a JOIN C b on a.i = b.i)
List l2 = q2.list();
тогда вы можете добавить оба списка ->
l1.addAll(l2);
Объединение будет в процессе, а не базой данных, это то же самое, что и предложение Уолта.
итак, а что, если вы хотите отсортировать здесь? вы можете в конечном итоге дублировать механизм сортировки, реализовав другой алгоритм слияния. очень не приятно
Как сказал Патрик, добавление СПИСОК из каждого ВЫБРАТЬ было бы хорошей идеей, но помните, что это действует как СОЮЗ ВСЕ. Чтобы избежать этого побочного эффекта, просто проверьте, добавлен ли объект в окончательную коллекцию или нет. Если нет, то добавь.
Еще кое-что, о чем вам следует позаботиться, - это то, что если у вас есть ПРИСОЕДИНИТЬСЯ в каждом ВЫБРАТЬ, результатом будет список массивов объектов (List<Objetc[]>), поэтому вам нужно перебирать его, чтобы сохранить только тот объект, который вам нужен.
Надеюсь, что это работает.
Это особый случай, но он может вдохновить вас на создание собственной работы. Цель здесь - подсчитать общее количество записей из двух разных таблиц, где записи соответствуют определенным критериям. Я считаю, что этот метод будет работать в любом случае, когда вам нужно агрегировать данные из нескольких таблиц / источников.
У меня есть несколько специальных промежуточных классов, поэтому код, вызывающий именованный запрос, короткий и приятный, но вы можете использовать любой метод, который вы обычно используете, в сочетании с именованными запросами для выполнения вашего запроса.
QueryParms parms=new QueryParms();
parms.put("PROCDATE",PROCDATE);
Long pixelAll = ((SourceCount)Fetch.row("PIXEL_ALL",parms,logger)).getCOUNT();
Как вы можете видеть здесь, именованный запрос начинает ужасно напоминать оператор union:
@Entity
@NamedQueries({
@NamedQuery(
name = "PIXEL_ALL",
query = "" +
" SELECT new SourceCount(" +
" (select count(a) from PIXEL_LOG_CURR1 a " +
" where to_char(a.TIMESTAMP, 'YYYYMMDD') = :PROCDATE " +
" )," +
" (select count(b) from PIXEL_LOG_CURR2 b" +
" where to_char(b.TIMESTAMP, 'YYYYMMDD') = :PROCDATE " +
" )" +
") from Dual1" +
""
)
})
public class SourceCount {
@Id
private Long COUNT;
public SourceCount(Long COUNT1, Long COUNT2) {
this.COUNT = COUNT1+COUNT2;
}
public Long getCOUNT() {
return COUNT;
}
public void setCOUNT(Long COUNT) {
this.COUNT = COUNT;
}
}
Частью волшебства здесь является создание фиктивной таблицы и вставка в нее одной записи. В моем случае я назвал его dual1, потому что моя база данных - Oracle, но я не думаю, что имеет значение, как вы называете фиктивную таблицу.
@Entity
@Table(name = "DUAL1")
public class Dual1 {
@Id
Long ID;
}
Не забудьте вставить фиктивную запись:
SQL> insert into dual1 values (1);
Одно из решений - использовать агрегированный объект, как описано здесь.