Использование графа сущности JPA со сложными условиями
У меня есть проект, построенный на Spring MVC + JPA + Hibernate. Я использую диаграммы сущностей (JPA 2.1) для определения данных для извлечения из базы данных, как в примере ниже.
EntityGraph<Company> entityGraph = entityManager.createEntityGraph(Company.class);
entityGraph.addAttributeNodes("reviews");
Map<String, Object> hints = new HashMap<String, Object>();
hints.put("javax.persistence.loadgraph", entityGraph);
Company company = entityManager.find(Company.class, companyId, hints);
Мой объект Review
имеет связь с объектом Company
(ManyToOne
).
Здесь я просто извлекаю объект Company
с заполненной коллекцией reviews
. Это хорошо работает в сценариях, подобных описанным выше. Но что, если я хочу получить все или некоторые обзоры данной компании? То есть объекты Review
, которые связаны с компанией с данным идентификатором. Я хочу List<Review>
вместо объекта Company
с List<Review>
. Это всего лишь пример - в основном я ищу больше гибкости, чем просто поиск объекта на основе первичного ключа. Я могу сделать это с помощью HQL без проблем, но тогда мне придется писать несколько похожих запросов, в зависимости от того, какие данные мне нужны в конкретном контексте.
Метод find
на javax.persistence.EntityManager
просто позволяет запросить объект на основе первичного ключа. Но возможно ли использовать графики сущностей в более сложных сценариях, например. с объектами Criteria или запросами HQL? Например, поиск объектов с другими условиями, чем с помощью первичного ключа, возможно, даже для условий ассоциации.
Надеюсь, я поняла. Спасибо заранее!
Ответы
Ответ 1
То, что вы ищете, возможно, не EntityGraph
s, но JPA Query
(либо JPQL в форме NamedQuery
, либо CriteriaQuery
). Все это является частью спецификации JPA.
Итак, в основном вы можете:
- Аннотировать все сущности clas с помощью
@NamedQueries
, чтобы указать запросы JPQL. Их преимущество заключается в том, что их синтаксис проверяется при развертывании (развертывание не будет выполнено, если, например, NamedQueries получит доступ к отсутствующим свойствам), и они многократно используются, а недостатком является: они статически определены (но, конечно, принимают параметры).
- Построить во время выполнения запросов JPQL с EntityManager. Я использую NamedQueries чаще, чем запросы времени выполнения из-за вышеупомянутых преимуществ.
- Используйте API критериев, который имеет то преимущество, что они безопасны по типу при подключении/поиске/добавлении условий/воспроизведении с реальными объектами Java.
Теперь о EntityGraph
s: они просто помогают, поэтому вы получите дополнительные поля из запроса (независимо от того, используете ли вы EntityManager.find()
с параметром карты дополнительных свойств или Query.setHints()
). Вы также можете использовать подграфы для более сложных ситуаций. Проверьте этот и этот пример.
Ответ 2
Как вы заметили:
Я могу сделать это с помощью HQL без проблем, но тогда мне придется писать несколько похожих запросов, в зависимости от того, какие данные мне нужны в конкретном контекст.
Я предполагаю, что окончательное решение будет выглядеть примерно так, как показано ниже, где может быть определен один метод запроса, но к которому мы можем передавать оба динамических критерия (будь то прямые или вложенные свойства объекта) и план динамической выборки, определяющий эти EntityGraphs для активации:
public interface CompanyRepository{
List<Review> findAll(Criteria criteria, FetchPlan fetchPlan);
}
Проект Spring Data действительно может заставить нас участвовать в этом, но не в настоящее время,
(хотя есть некоторые обсуждения вокруг недостающей части: см. ниже).
В настоящий момент это первая часть:
public interface CompanyRepository{
List<Review> findAll(Criteria criteria);
}
либо с помощью шаблона Спецификации (http://docs.spring.io/spring-data/jpa/docs/1.8.0.M1/reference/html/#specifications), либо,
проще, используя API-интерфейс QueryDSL, альтернативный API-интерфейсу JPA.
Используя подход QueryDSL, мы можем создать определение репозитория следующим образом:
public interface ReviewRepository extends CrudRepository<Review, Long>, QueryDslPredicateExecutor<Review>{
}
Теперь и без создания реализации (созданной каркасом) или написания кода мы можем назвать его ниже,
с любой комбинацией атрибутов:
Review review = respository.findOne();
Iterable<Review> reviews = respository.findAll(QReview.review.created.eq(someDate));
Iterable<Review> reviews = respository.findAll(QReview.review.created.eq(someDate).and(QReview.review.creator.forename.eq("Jim"));
Iterable<Review> reviews = respository.findAll(QReview.review.created.eq(someDate).and(QReview.review.creator.forename.eq("Jim").and(QReview.review.company.name.eq("Amazon"));
и т.д....
где QReview - это тип запроса, созданный QueryDSL-библиотекой, и дающий вам строго типизированные запросы, а методы findOne (предикат предикатов) и findAll (Precicate predicate) наследуются от:
http://docs.spring.io/spring-data/commons/docs/current/api/org/springframework/data/querydsl/QueryDslPredicateExecutor.html
Это дает вам много для очень небольшого (нулевого) кода, кроме создания определений интерфейса, но все равно потребует дополнительного
(субоптимальный с точки зрения взаимодействия БД) механизм обработки ленивой загрузки, т.е. один из механизмов, выделенных в других ваших недавних
вопрос и который привел к этому.
Однако я не вижу, почему Spring Data не удалось обновить для обработки динамической спецификации EntityGraphs, и действительно,
некоторые обсуждения вокруг этого, поэтому он может быть в стадии разработки:
http://forum.spring.io/forum/spring-projects/data/108202-custom-fetch-groups-with-spring-data-jpa-possible
возможно, стоит собрать JIRA, чтобы узнать, планируют ли это что-то или спрашивают
https://stackoverflow.com/users/18122/oliver-gierke
непосредственно.
Некоторая поддержка была добавлена в Spring Data для новой функции JPA 2.1 EntityGraphb, но я не вижу, что ее можно заставить работать с общим методом запросов, который мы хотим здесь:
http://docs.spring.io/spring-data/jpa/docs/1.8.0.M1/reference/html/#jpa.entity-graph