Как объединить несвязанные объекты с помощью API JPA Criteria

Две таблицы базы данных имеют отношение внешнего ключа.

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

Можно ли с помощью API-интерфейса критериев JPA создать запрос, объединяющий две таблицы?

Во всех примерах, которые я нашел в Интернете, для достижения цели используется столбец соединения, но, как указано выше, он был удален из кода, поскольку в большинстве случаев меня не интересуют отношения между A и . B и я боюсь возможных накладных расходов.

Ответ 1

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

Во всяком случае, для создания запроса, который использует несколько несвязанных объектов, вам нужно поместить их как объекты from (root) (как и в SQL или JPQL).

SELECT .... FROM Link l, Training t WHERE l.attribute = t.attribute;

Root<Link> rootLink = criteriaQuery.from(Link.class);
Root<Training> rootTraining = criteriaQuery.from(Training.class);
...
criteriaQuery.where(
    criteriaBuilder.equal(rootLink.get(link_.linkAttribute), trainingLink));

Ответ 2

Присоединение к несвязанным объектам не охватывается последней спецификацией JPA (2.1)

Однако Hibernate 5.1. 0+ и EclipseLink 2.4. 0+ поддерживают специальные объединения. http://blog.anthavio.net/2016/03/join-unrelated-entities-in-jpa.html

Другая возможность - нативный запросhttp://www.oracle.com/technetwork/articles/vasiliev-jpql-087123.html

Ответ 3

Как я объяснял в этой статье, начиная с Hibernate 5.1, вы можете объединять несвязанные сущности при использовании JPQL и HQL:

Tuple postViewCount = entityManager.createQuery(
    "select p as post, count(pv) as page_views " +
    "from Post p " +
    "left join PageView pv on p.slug = pv.slug " +
    "where p.title = :title " +
    "group by p", Tuple.class)
.setParameter("title", "Presentations")
.getSingleResult();

Однако эта функция недоступна в Criteria API, так как для этого потребуется расширение API.

Решение

Хотя вы можете попытаться использовать два объекта Root и эмулировать INNER JOIN с помощью предикатов предложения WHERE, полученный SQL-код не является лучшим способом решения этой проблемы.

Вам следует рассмотреть возможность использования jOOQ, поскольку, помимо предоставления вам возможности объединять таблицы любыми возможными способами, вы также можете извлекать сущности, если передаете сгенерированный запрос SQL в метод JPA createNativeQuery.

Ответ 4

Вы можете использовать реализацию Blaze-persistence в JPA Criteria, которая переводится в JPQL/HQL и предлагает эту функцию в качестве расширения API JPA Criteria.

Ищите "зависимости модуля Blaze-Persistence JPA-Criteria" в файле readme: https://github.com/Blazebit/blaze-persistence/blob/master/README.md

Затем используйте BlazeCriteriaBuilder cb = BlazeCriteria.get(criteriaBuilderFactory) и, наконец, blazeCriteriaQuery.getCriteriaBuilder(entityManager), чтобы визуализировать объект JPA Criteria Query.