Альтернативы союза спящего режима
Какими альтернативами я должен реализовать запрос объединения с использованием спящего режима? Я знаю, что hibernate не поддерживает запросы объединения на данный момент, сейчас единственный способ, с помощью которого я вижу объединение, - использовать таблицу представлений.
Другой вариант заключается в использовании простого jdbc, но таким образом я бы потерял все мои положительные результаты в примере/критериях, а также проверку валидации спящего режима, которая спящий выполняет против таблиц/столбцов.
Ответы
Ответ 1
Используйте VIEW. Те же классы могут быть сопоставлены с разными таблицами/представлениями с использованием имени сущности, поэтому у вас даже не будет много дублирования. Быть там, сделано, работает нормально.
У простой JDBC есть еще одна скрытая проблема: он не знает о кеш-ке в сеансе Hibernate, поэтому, если что-то кэшируется до конца транзакции и не очищается от сеанса Hibernate, запрос JDBC его не найдет. Иногда может быть очень загадочным.
Ответ 2
Вы можете использовать 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);
Часто бывает лучше сделать два простых запроса, чем один сложный.
EDIT:
чтобы привести пример, вот вывод 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. строка не использует индекс и рассматривает строки 200k+. Плохо! Выполнение этого запроса заняло 0,7 с, оба подзапроса находятся в миллисекундах.
Ответ 3
Я должен согласиться с Владимиром. Я тоже изучал использование UNION в HQL и не мог найти пути вокруг него. Странная вещь заключалась в том, что я мог найти (в Hibernate FAQ), что UNION не поддерживается, отчеты об ошибках, относящиеся к UNION, обозначенные как "фиксированные", группы новостей людей, которые говорят, что заявления будут усечены в UNION, а также другие группы новостей людей, сообщающих об этом хорошо...
После того, как я ударил его, я закончил перенос моего HQL обратно на обычный SQL, но сделать это в представлении в базе данных было бы хорошим вариантом. В моем случае части запроса были динамически сгенерированы, поэтому мне пришлось строить SQL в коде.
Ответ 4
У меня есть решение для одного критического сценария (для которого я много боролся) с объединением в 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();
то u может добавить оба списка →
l1.addAll(l2);
Ответ 5
Представление - лучший подход, но поскольку hql обычно возвращает список или набор... вы можете сделать list_1.addAll(list_2). Полностью сосет по сравнению с профсоюзом, но должен работать.
Ответ 6
Возможно, у меня была более прямая проблема. Мой "например" был в JPA с Hibernate в качестве поставщика JPA.
Я разделил три выбора (два во втором случае) на несколько выборок и объединил собранные коллекции, эффективно заменив "union all".
Ответ 7
Я тоже пережил эту боль - если запрос динамически генерируется (например, Hibernate Criteria), тогда я не мог найти практический способ сделать это.
Хорошей новостью для меня было то, что я изучал профсоюз только для решения проблемы производительности при использовании "или" в базе данных Oracle.
Решение Patrick опубликовано (комбинируя результаты программно с использованием набора), в то время как уродливый (особенно потому, что я хотел выполнить подкачку результатов), был адекватен мне.
Ответ 8
Как сказал Патрик, добавление LIST s из каждого SELECT было бы хорошей идеей, но помните, что оно действует как UNION ALL. Чтобы избежать этого побочного эффекта, просто контролируйте, если объект уже добавлен в окончательный сбор или нет. Если нет, добавьте его.
Что-то еще, что вам нужно заботиться, это то, что если у вас есть JOIN в каждом SELECT, результатом будет список массива объектов (List<Objetc[]>
), поэтому вам нужно перебирать его, чтобы сохранить только тот объект, который вам нужен.
Надеюсь, что это сработает.
Ответ 9
Вот специальный случай, но может вдохновить вас на создание собственной работы. Цель здесь - подсчитать общее количество записей из двух разных таблиц, где записи соответствуют определенным критериям. Я считаю, что этот метод будет работать в любом случае, когда вам необходимо агрегировать данные из нескольких таблиц/источников.
У меня есть специальная настройка промежуточных классов, поэтому код, вызывающий именованный запрос, является коротким и приятным, но вы можете использовать любой метод, который вы обычно используете в сочетании с именованными запросами, для выполнения вашего запроса.
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);