Hibernate Союз альтернатив - PullRequest
       21

Hibernate Союз альтернатив

52 голосов
/ 14 октября 2008

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

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

Ответы [ 9 ]

63 голосов
/ 15 октября 2010

Вы можете использовать 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. row не использует никакого индекса и рассматривает 200k + строк. Плохой! Выполнение этого запроса заняло 0,7 с, тогда как оба подзапроса находятся в миллисекундах.

28 голосов
/ 15 октября 2008

Используйте VIEW. Одни и те же классы могут быть сопоставлены с различными таблицами / представлениями с использованием имени сущности, поэтому у вас даже не будет большого дублирования. Быть там, сделать это, работает хорошо.

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

6 голосов
/ 16 октября 2008

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

4 голосов
/ 29 мая 2015

У меня есть решение для одного критического сценария (за который я много боролся) с объединением в HQL.

например. Вместо того, чтобы не работать: -

select i , j from A a  , (select i , j from B union select i , j from C) d where a.i = d.i 

OR

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);
3 голосов
/ 16 октября 2008

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

2 голосов
/ 09 марта 2010

Возможно, мне нужно было решить более простую задачу. Мой «например» был в JPA с Hibernate в качестве поставщика JPA.

Я разделил три выбора (два в втором случае) на множественный выбор и объединил возвращенные мной коллекции, фактически заменив «объединение всех».

0 голосов
/ 14 июля 2017

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

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

QueryParms parms=new QueryParms();
parms.put("PROCDATE",PROCDATE);

Long pixelAll = ((SourceCount)Fetch.row("PIXEL_ALL",parms,logger)).getCOUNT();

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

@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);
0 голосов
/ 18 ноября 2015



Как сказал Патрик, было бы неплохо добавить LIST с каждого SELECT , но помните, что он действует как UNION ALL . Чтобы избежать этого побочного эффекта, просто проверьте, добавлен ли объект в окончательную коллекцию или нет. Если нет, то добавьте его.
Еще кое-что, о чем вам следует позаботиться, это то, что если у вас есть JOIN в каждом SELECT , результатом будет список массивов объектов (List<Objetc[]>). ) поэтому вам придется перебирать его, чтобы сохранить только тот объект, который вам нужен.

Надеюсь, что это работает.

0 голосов
/ 23 января 2009

Я тоже прошел через эту боль - если запрос генерируется динамически (например, Критерии гибернации), то я не могу найти практический способ сделать это.

Хорошей новостью для меня было то, что я исследовал объединение только для решения проблемы производительности при использовании «или» в базе данных Oracle.

Решение, которое выложил Патрик (комбинируя результаты программно с использованием набора), в то время как некрасиво (тем более, что я тоже хотел делать разбивку по результатам), было для меня адекватным.

...