2016-03-22 2 views
0

У меня есть объект, который отображает в виде таблицы, определенной таким образом:Как использовать JPA criteriaBuilder поиск по атрибутам в коллекции суб-атрибутов

@Entity 
@Table(name = "cmmn_calendar_evnt") 

public class CommonCalendarEvent implements java.io.Serializable 
{ 
    private Integer      cevId; 
    private Set<CommonCalendarEventPart> commonCalendarEventParts = new HashSet<CommonCalendarEventPart>(0) 

@Id 
    @GeneratedValue(strategy = IDENTITY) 
    @Column(name = "CEV_ID", unique = true, nullable = false) 
    public Integer getCevId() 
    { 
     return this.cevId; 
    } 

@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "commonCalendarEvent") 
    public Set<CommonCalendarEventPart> getCommonCalendarEventParts() 
    { 
     return this.commonCalendarEventParts; 
    } 
} 

и CommonCalendarEventPart определяется следующим образом:

@Entity 
@Table(name = "cmmn_calendar_evnt_part") 

public class CommonCalendarEventPart implements java.io.Serializable 
{ 

    private static final long serialVersionUID = 1L; 
    private Integer    ceeId; 
    private CommonCalendarEvent commonCalendarEvent; 
    private PartParticipant  partParticipant; 

    @Id 
    @GeneratedValue(strategy = IDENTITY) 
    @Column(name = "CEE_ID", unique = true, nullable = false) 
    public Integer getCeeId() 
    { 
     return this.ceeId; 
    } 

    @ManyToOne(fetch = FetchType.LAZY) 
    @JoinColumn(name = "CEE_CEV_ID", nullable = false) 
    public CommonCalendarEvent getCommonCalendarEvent() 
    { 
     return this.commonCalendarEvent; 
    } 

     @ManyToOne(fetch = FetchType.LAZY) 
    @JoinColumn(name = "CEE_PPT_ID", nullable = false) 
    public PartParticipant getPartParticipant() 
    { 
     return this.partParticipant; 
    } 

} 

и, наконец:

@Entity 
@Table(name = "part_participant") 

public class PartParticipant implements java.io.Serializable 
{ 
    private static final long   serialVersionUID   = 1L; 
    private Integer      pptId; 

    @Id 
    @GeneratedValue(strategy = IDENTITY) 
    @Column(name = "PPT_ID", unique = true, nullable = false) 
    public Integer getPptId() 
    { 
     return this.pptId; 
    } 

} 

Я хочу использовать CriteriaBuilder для создания запроса, находя все CommonCalendarEvent для конкретного идентификатора участника.

В HQL это будет выглядеть примерно так: (хотя я не подтвердил, что это HQL правильно либо)

"from commonCalendarEvent cce where :pptId in (cce.commonCalendarEventParts.partParticipant.pptId)" 

Я попробовал несколько подходов, что я думал, были интуитивные попытки в написании criteriaBuilder но мои попытки привели к ошибкам от «неожиданного конца поддерева» до ошибок реализации.

..... 

    CriteriaBuilder builder = getEntityManager().getCriteriaBuilder(); 

    CriteriaQuery<CommonCalendarEvent> criteria = builder.createQuery(CommonCalendarEvent.class); 

    Root<CommonCalendarEvent> root = criteria.from(CommonCalendarEvent.class); 

    Fetch<CommonCalendarEvent, CommonCalendarEventPart> evf = root.fetch(CommonCalendarEvent_.commonCalendarEventParts, JoinType.LEFT); 

    Join<CommonCalendarEvent, CommonCalendarEventPart> evj = (Join<CommonCalendarEvent, CommonCalendarEventPart>) evf; 

    Join<CommonCalendarEventPart, PartParticipant> evpj = evj.join(CommonCalendarEventPart_.partParticipant); 
    List<Predicate> pred = new ArrayList<Predicate>(); 

    pred.add(builder.equal(evpj.get(PartParticipant_.pptId), pptId)); 
    criteria.where(builder.and(pred.toArray(new Predicate[] {}))); 
    return getEntityManager().createQuery(criteria).getResultList(); 

............. 

выше дает ошибку «неожиданный конец поддерева».

Любая помощь приветствуется.

+0

Просмотрите представленный код: what is 'evf'? – perissf

+0

Извините скорректированный код. забыли добавить Fetch evf ... его там сейчас. – Mike

+0

Посмотри мой обновленный ответ – perissf

ответ

0

Fetch<CommonCalendarEvent, CommonCalendarEventPart> evf не нужно, а первое присоединиться заявление должно быть исправлено:

Join<CommonCalendarEvent, CommonCalendarEventPart> evj = 
          root.join(CommonCalendarEvent_.commonCalendarEventParts); 

Остальная часть запроса кажется правильным.

+0

Извините, забыл положить в строку: Fetch evf = ..... Его там сейчас. но даже с этим, или даже если я запускаю код с помощью Join, а не извлечения. Я получаю ошибку «неожиданный конец поддерева». – Mike

+0

Нужно предположить, что, поскольку я получаю исключения Runtime, которые должен был скомпилировать код. – Mike

+0

Ну, Fetch необходим, если я хочу, чтобы данные CommomCalendarEventPart возвращались в результаты. Итак, да, требуется Fetch. Затем я возвращаюсь к соединению, чтобы присоединиться к остальным данным. Но Event, если я не делаю Fetch и вместо этого выполняю соединение, я все равно получаю ту же ошибку. Поэтому должно быть что-то, что позволяет мне рассматривать коллекцию CommonCalendarEventParts таким же образом, как и решение Hql. – Mike

1

+1 для использования ленивой инициализации. Модель JPA - это Object или Entity, поэтому вам нужно привыкнуть к мысли в этих терминах. A PartParticipant не идентифицирован по его id в JPA, а сам объект. Предполагая, что у вас есть список участников:

PartParticipant pp = em.find(PartParticipant.class, 2); 
List<PartParticipant> pps = new ArrayList<PartParticipant>(); 
pps.add(pp); 

Затем вы передаете этот список запросам. В JPQL:

TypedQuery<CommonCalendarEvent> cev = em.createQuery("select cev from CommonCalendarEvent cev join fetch cev.commonCalendarEventParts cce where cce.partParticipant in :pps", CommonCalendarEvent.class); 
List<CommonCalendarEvent> cevs = cev.setParameter("pps", pps).getResultList(); 

Примечание выборки необходим для предотвращения LazyInitializationExceptions.

Зная JPQL, то CriteriaQuery должны следовать в значительной степени то же самое:

CriteriaBuilder cb = em.getCriteriaBuilder(); 
CriteriaQuery<CommonCalendarEvent> q = cb.createQuery(CommonCalendarEvent.class); 
Root<CommonCalendarEvent> r = q.from(CommonCalendarEvent.class); 
Join<CommonCalendarEvent, CommonCalendarEventPart> j = r.join("commonCalendarEventParts"); 
r.fetch("commonCalendarEventParts"); 
q.select(r).where(j.get("partParticipant").in(pps)); 
List<CommonCalendarEvent> rs = em.createQuery(q).getResultList(); 

Вам не нужно делать ничего особенного с выборки другой, чем выполнить его. Как вы можете видеть, запрос использует идентификатор PartParticipant.

select 
    commoncale0_.CEV_ID as CEV_ID1_0_0_, 
    commoncale1_.CEE_ID as CEE_ID1_1_1_, 
    commoncale1_.CEE_CEV_ID as CEE_CEV_2_1_1_, 
    commoncale1_.CEE_PPT_ID as CEE_PPT_3_1_1_, 
    commoncale1_.CEE_CEV_ID as CEE_CEV_2_0_0__, 
    commoncale1_.CEE_ID as CEE_ID1_1_0__ 
from cmmn_calendar_evnt commoncale0_ 
    inner join cmmn_calendar_evnt_part commoncale1_ on commoncale0_.CEV_ID=commoncale1_.CEE_CEV_ID 
where commoncale1_.CEE_PPT_ID in (?) 
+0

очень красиво. – Mike

+0

очень красиво. Я собирался опубликовать сообщение, что обнаружил, что не могу взять Fetch и передать его в Join, потому что Fetch дает общее соединение, но законное соединение дает «SetJoin», который мне нужен. Поэтому мое решение состоит в том, чтобы делать то, что вы делаете, иметь как соединение, так и выборку. Но в моем entityManager я получаю два внутренних соединения кода. – Mike

+0

1 вопрос хотя. Как система знает, чтобы решить совпадение с PartParticipant только с PPTID? это из-за тега @ID? если бы я прошел в PartParticipant, у которого не было ID, это не сработает правильно? – Mike