Подтвердить что ты не робот

Альтернативы союза спящего режима

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

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

4b9b3361

Ответ 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);