Используя API-интерфейс JPA Criteria, можете ли вы выполнить выборку, в результате чего получается только одно соединение?
Использование JPA 2.0. По-видимому, по умолчанию (без явной выборки) поля @OneToOne(fetch = FetchType.EAGER)
выбираются в 1 + N запросах, где N - количество результатов, содержащих объект, который определяет отношение к отдельному связанному объекту. Используя API критериев, я могу попытаться избежать этого следующим образом:
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<MyEntity> query = builder.createQuery(MyEntity.class);
Root<MyEntity> root = query.from(MyEntity.class);
Join<MyEntity, RelatedEntity> join = root.join("relatedEntity");
root.fetch("relatedEntity");
query.select(root).where(builder.equals(join.get("id"), 3));
В идеале это должно быть эквивалентно следующему:
SELECT m FROM MyEntity m JOIN FETCH myEntity.relatedEntity r WHERE r.id = 3
Однако запрос критериев приводит к тому, что корневая таблица без необходимости соединяется с таблицей связанных сущностей дважды; один раз для извлечения и один раз для предиката. Полученный SQL выглядит примерно так:
SELECT myentity.id, myentity.attribute, relatedentity2.id, relatedentity2.attribute
FROM my_entity myentity
INNER JOIN related_entity relatedentity1 ON myentity.related_id = relatedentity1.id
INNER JOIN related_entity relatedentity2 ON myentity.related_id = relatedentity2.id
WHERE relatedentity1.id = 3
Увы, если я только делаю выборку, то у меня нет выражения для использования в предложении where.
Я что-то упустил, или это ограничение API критериев? Если это последнее, исправляется ли это в JPA 2.1 или существуют ли какие-либо улучшения для конкретного поставщика?
В противном случае, кажется, лучше просто отказаться от проверки типа компиляции (я понимаю, что мой пример не использует метамодель) и использует динамический JPQL TypedQueries.
Ответы
Ответ 1
Вместо root.join(...)
вы можете использовать root.fetch(...)
, который возвращает объект Fetch<>
.
Fetch<>
является потомком Join<>
, но его можно использовать аналогичным образом.
Вам просто нужно отбрасывать Fetch<>
в Join<>
, он должен работать для EclipseLink и Hibernate
...
Join<MyEntity, RelatedEntity> join = (Join<MyEntity, RelatedEntity>)root.fetch("relatedEntity");
...
Ответ 2
Начиная с JPA 2.1, вы можете использовать для этого динамические графы объектов. Удалите вашу выборку и укажите граф сущности следующим образом:
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<MyEntity> query = builder.createQuery(MyEntity.class);
Root<MyEntity> root = query.from(MyEntity.class);
Join<MyEntity, RelatedEntity> join = root.join("relatedEntity");
query.select(root).where(builder.equal(join.get("id"), 3));
EntityGraph<MyEntity> fetchGraph = entityManager.createEntityGraph(MyEntity.class);
fetchGraph.addSubgraph("relatedEntity");
entityManager.createQuery(query).setHint("javax.persistence.loadgraph", fetchGraph);
Ответ 3
Использование root.fetch()
в EclipseLink создаст SQL с INNER JOIN
, потому что есть 3 типа, а по умолчанию INNER.
INNER, LEFT, RIGHT;
Предполагается использовать CreateQuery.
TypedQuery<T> typedQuery = entityManager.createQuery(query);
РЕДАКТИРОВАТЬ: вы можете вставить корень в From следующим образом:
From<?, ?> join = (From<?, ?>) root.fetch("relatedEntity");