Как избежать ненужных выборок и объединений в HQL и критериях

Я пробовал разные комбинации HQL и критериев, и мне не удалось избежать ненужных объединений (в обоих) и некоторых ненужных выбирает (в критериях).

В нашем сценарии у нас есть связь @ManyToMany между объектами Сегмент и Приложение (навигация осуществляется от Сегмента к Приложениям).

Сначала я попробовал эти критерии:

Application app = ...
List<Segment> segments = session.createCriteria(Segment.class)
    .createCriteria(Segment.APPLICATIONS)
    .add(Restrictions.idEq(app.getId()))
    .list();

Что производит этот SQL:

select
    this_.id as id1_1_,
    this_.description as descript2_1_1_,
    this_.name as name1_1_,
    applicatio3_.segment_id as segment1_1_,
    applicatio1_.id as app2_,               <==== unnecessary APPLICATIONS columns
    applicatio1_.id as id7_0_,
    applicatio1_.name as name7_0_,
    applicatio1_.accountId as accountId7_0_,
    applicatio1_.applicationFlags as applicat5_7_0_,
    applicatio1_.description_ as descript6_7_0_,
from
    SEGMENTS this_ 
inner join
    SEGMENTS_APPLICATIONS applicatio3_ 
        on this_.id=applicatio3_.segment_id 
inner join                                       <==== unnecessary join
    APPLICATIONS applicatio1_ 
        on applicatio3_.app_id=applicatio1_.id 
where
    applicatio1_.id = ?

Как видите, Criteria выбирает столбцы из APPLICATIONS, которые я не хочу выбирать. Я не нашел способа сделать это (возможно ли это?). Кроме того, он соединяется с ПРИЛОЖЕНИЯМИ, что, на мой взгляд, необязательно, поскольку идентификатор приложения уже находится в таблице соединения SEGMENTS_APPLICATIONS (то же самое происходит с HQL).

(В качестве дополнительного сомнения я хотел бы знать ограничение, которое использует приложение напрямую, а не app.getId (). Как вы увидите, я мог бы сделать это в HQL-версии запроса)

Поскольку я не мог ограничить выделенную часть (мне не нужны свойства приложения), я попробовал этот HQL с предложением «select»:

Application app = ...
List<Segment> segments = session.createQuery(
    "select s from Segment s join s.applications as app where app = :app")
    .setParameter("app", app)
    .list();

который производит:

select
    segment0_.id as id1_,
    segment0_.description as descript2_1_,
    segment0_.name as name1_,
from
    SEGMENTS segment0_ 
inner join
    SEGMENTS_APPLICATIONS applicatio1_ 
        on segment0_.id=applicatio1_.segment_id 
inner join                                        <==== unnecessary join
    APPLICATIONS applicatio2_ 
        on applicatio1_.app_id=applicatio2_.id 
where
    applicatio2_.id=? 

Вы можете видеть, что HQL не выбирает свойства из приложения (благодаря части "select s"), но по-прежнему присоединяется к таблице APPLICATIONS, что я считаю ненужным. Как этого избежать?

(В качестве примечания обратите внимание, что в HQL я мог использовать приложение напрямую, а не app.getId (), как в критериях)

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

(Этот пример с @ManyToMany, но я думаю, что это также происходит с @OneToMany, а также с @ManyToOne и @OneToOne, даже с fetch = LAZY).

Большое спасибо, Ферран


person Ferran Maylinch    schedule 19.05.2012    source источник


Ответы (1)


Дополнительные выбранные столбцы при использовании критериев происходят из-за давней ошибки в гибернации. AFAIK, единственный способ избежать этого - использовать HQL или API критериев JPA2.

Другая проблема также обозначена как ошибка, но она оказывает меньшее влияние, и я было бы наплевать на это.

person JB Nizet    schedule 19.05.2012
comment
Вау, спасибо тебе большое. Давний баг? Не могу поверить, что они еще не исправили это. Просто потому, что есть обходной путь с HQL? - person Ferran Maylinch; 19.05.2012
comment
Я тоже не могу в это поверить :-( OTOH, это программное обеспечение с открытым исходным кодом, и они, вероятно, были заняты реализацией новой реализации критериев JPA2, которая, вероятно, должна быть предпочтительнее этого старого проприетарного API. - person JB Nizet; 19.05.2012
comment
О, не могли бы вы порекомендовать нам начать использовать JPA2 вместо старого Criteria API? Можно и то, и другое смешивать? (Просто чтобы избежать рефакторинга старого кода). - person Ferran Maylinch; 19.05.2012
comment
Я ничего не рекомендую :-) Но если бы мне пришлось начинать новый проект с нуля, я бы использовал стандартный JPA API и новый Criteria API. - person JB Nizet; 19.05.2012
comment
@JB nizet Упоминание об этой ошибке избавило меня от множества головокружительных размышлений о том, почему в запросе критериев было бесполезно оставлено внешнее присоединение к таблице ссылок и замедление запроса на порядок. HQL переписать это есть! - person RedYeti; 06.12.2012