Как я могу сделать подзапрос в статистике JPA Criteria Builder Select? - PullRequest
0 голосов
/ 04 февраля 2020

Я пытаюсь сделать оператор select, используя CritierBuilder / CrtieriaQuery, чтобы выбрать определенные поля из таблицы A, а затем логический флаг, если эта запись существует в другой таблице.

По сути, у меня есть список «офицеров» и список пользователей. Пользователи - это люди, использующие систему, которые могут добавить в закладки / сохранить сотрудника. Когда пользователь запрашивает сотрудников, я хочу показать, какие из них были добавлены в закладки.

SELECT o.FIRST_NAME, o.LAST_NAME,
 (select CAST(1 AS BIT) from OFFICER_BOOKMARK b where b.OFFICER_ID=o.OFFICER_ID AND USER_ID=123456789) as BOOKMARKED 
from OFFICER o;

Итак, этот запрос я запустил в своей консоли базы данных h2, и он (довольно) очень хорошо работает. Возвращается значение true, если сотрудник отмечен закладкой пользователя 123456789, а в противном случае для столбца, добавленного в закладки, установлено значение null.

Но у меня возникают проблемы с переводом этого в запросы критериев jpa ...

public List<OfficerDTO> getOfficersDto() {
    CriteriaBuilder cb = em.getCriteriaBuilder();
    CriteriaQuery<OfficerDTO> cq = cb.createQuery(OfficerDTO.class);
    Root<OfficerEntity> root = cq.from(OfficerEntity.class);
    Root<OfficerBookmarkEntity> subRoot = cq.from(OfficerBookmarkEntity.class);

    Subquery<Boolean> subquery = cq.subquery(Boolean.class);
    subRoot.alias("bookmarked");
    subquery.select(cb.isNotNull(subRoot.get("id")));
    subquery.where(cb.equal(subRoot.get("officer").get("officerId"), root.get("officerId")));
    subquery.where(cb.equal(subRoot.get("user").get("userId"), "123456789"));

    cq.multiselect(
            cb.construct(
                OfficerDTO.class,
                root.get("firstName"),
                root.get("lastName"),
                subquery.getSelection().as(Boolean.class)
            )
    );
    TypedQuery<OfficerDTO> q = em.createQuery(cq);
    return q.getResultList();
}

Я думаю, что я довольно близок, но я не могу понять часть подзапроса для select и как просто вернуть логическое значение.

1 Ответ

1 голос
/ 05 февраля 2020

Проблема в том, что cb.isNotNull(subRoot.get("id")) работает, только если subquery результат существует (возвращает только true). В противном случае у вас есть null. Таким образом, вы должны проверить subquery результат на более высоком рычаге.

Это должно работать

  public List<OfficerDTO> getOfficersDto() {
        CriteriaBuilder cb = em.getCriteriaBuilder();
        CriteriaQuery<OfficerDTO> cq = cb.createQuery(OfficerDTO.class);
        Root<OfficerEntity> root = cq.from(OfficerEntity.class);

        Subquery<Long> subquery = cq.subquery(Long.class); // or Integer (depends on id class)
        Root<OfficerBookmarkEntity> subRoot = 
            subquery.from(OfficerBookmarkEntity.class);

        Predicate officerPredicate = cb.equal(
            subRoot.get("officer").get("officerId"), 
            root.get("officerId")
        );

        Predicate userPredicate = cb.equal(
            subRoot.get("user").get("userId"), 
            "123456789"
        );

        subquery.select(subRoot.get("id"))                 // select subRoot id
            .where(officerPredicate, userPredicate);       // if you execute `.where` twice 
                                                           // it replaces the previously added restrictions     
        cq.multiselect(
            cb.construct(
                OfficerDTO.class,
                root.get("firstName"),
                root.get("lastName"),
                subquery.getSelection().isNotNull()        // check if subquery result is present
            )
        );

        return em.createQuery(cq).getResultList();
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...