Альтернативы Hibernate Union

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

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

Одно из решений - использовать агрегированный объект, как описано здесь.

dma_k 09.11.2011 22:13
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
57
1
131 083
9
Перейти к ответу Данный вопрос помечен как решенный

Ответы 9

Ответ принят как подходящий

Используйте ПРОСМОТР. Одни и те же классы могут быть сопоставлены с разными таблицами / представлениями с использованием имени объекта, поэтому у вас даже не будет большого дублирования. Находясь там, сделал это, работает нормально.

У простого JDBC есть еще одна скрытая проблема: он не знает о кеше сеанса Hibernate, поэтому, если что-то было кэшировано до конца транзакции и не было сброшено из сеанса Hibernate, запрос JDBC не найдет его. Иногда это может быть очень загадочным.

И еще больше озадачивает, когда у вас есть кеш второго уровня, но вы используете простой JDBC (скажем, для отчетности) из какой-то другой части приложения.

javashlook 24.03.2009 01:27

Я должен согласиться с Владимиром. Я тоже изучал использование UNION в HQL и не мог найти способ обойти это. Странно то, что я смог найти (в FAQ по Hibernate), что UNION не поддерживается, отчеты об ошибках, относящиеся к UNION, помечены как «исправлено», группы новостей, в которых люди говорят, что утверждения будут усечены в UNION, и другие группы новостей людей, сообщающих, что это работает. отлично... После целого дня возни с ним, я закончил портирование моего HQL обратно на простой SQL, но сделать это в представлении в базе данных было бы хорошим вариантом. В моем случае части запроса генерировались динамически, поэтому мне пришлось вместо этого встроить SQL в код.

Спасибо, что перечислили различные теории, которые вы нашли, и какие из них не верны. Различия между тем, что «разумный разработчик» может ожидать, и тем, что от этого отличается, являются одними из наиболее полезных примечаний.

Mark Bennett 01.02.2012 00:46

Представление - лучший подход, но поскольку hql обычно возвращает список или набор ... вы можете сделать list_1.addAll (list_2). Полностью отстой по сравнению с профсоюзом, но должен работать.

Что насчет возврата другого типа? поэтому один список типа A и второй список типа B, но с одинаковыми свойствами (например, таблица A для данных в реальном времени и таблица B для исторических данных)

Matt Vegas 16.07.2019 11:54

Я тоже пережил эту боль - если запрос генерируется динамически (например, 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 секунды, если оба подзапроса находятся в миллисекундах.

Если вам нужна разбивка на страницы, вы можете использовать два подзапроса, не так ли?

Jose Ospina 19.05.2018 15:30

+1 от меня. Хотя часто лучше выполнить два простых запроса, чем один сложный, чаще лучше выполнить один запрос, который использует правильные индексы.

Pavel 28.06.2018 06:21

У меня есть решение для одного критического сценария (для которого я много боролся) с объединением в 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);

Объединение будет в процессе, а не базой данных, это то же самое, что и предложение Уолта.

Miguel Ping 29.05.2015 20:00

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

Bondax 05.07.2016 14:55



Как сказал Патрик, добавление СПИСОК из каждого ВЫБРАТЬ было бы хорошей идеей, но помните, что это действует как СОЮЗ ВСЕ. Чтобы избежать этого побочного эффекта, просто проверьте, добавлен ли объект в окончательную коллекцию или нет. Если нет, то добавь.
Еще кое-что, о чем вам следует позаботиться, - это то, что если у вас есть ПРИСОЕДИНИТЬСЯ в каждом ВЫБРАТЬ, результатом будет список массивов объектов (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);

Другие вопросы по теме