使用具有复杂条件的 JPA 实体图

Posted

技术标签:

【中文标题】使用具有复杂条件的 JPA 实体图【英文标题】:Using JPA entity graph with complex conditions 【发布时间】:2015-02-06 06:48:54 【问题描述】:

我有一个基于 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) 有关联。

这里我只是获取一个带有填充reviews 集合的Company 对象。这在上述情况下效果很好。但是,如果我想获取给定公司的全部或部分评论怎么办?也就是说,Review 对象与具有给定 ID 的公司相关联。我想要一个List&lt;Review&gt; 而不是带有List&lt;Review&gt;Company 对象。这只是一个例子——基本上我正在寻找比简单地基于主键查找对象更大的灵活性。我可以使用 HQL 毫无问题地做到这一点,但是我必须根据我在特定上下文中需要哪些数据来编写几个类似的查询。

javax.persistence.EntityManager 上的 find 方法可以简单地根据主键查询对象。但是是否有可能在更复杂的场景中使用实体图,例如使用 Criteria 对象或 HQL 查询?例如,通过主键以外的其他条件查找对象 - 甚至可能是关联条件。

我希望我说清楚了。提前致谢!

【问题讨论】:

【参考方案1】:

您正在寻找的可能不是EntityGraphs,而是JPA Query(JPQL 的形式为NamedQueryCriteriaQuery)。这都是 JPA 规范的一部分。

所以基本上你可以:

    @NamedQueries注释每个实体类以指定JPQL查询。它们的优点是在部署时会检查它们的语法(例如,如果 NamedQueries 访问缺少的属性,则部署将失败)并且可重用,缺点是:它们是静态定义的(但当然接受参数)。 使用 EntityManager 构造 on runtime JPQL queries。由于上述优点,我使用 NamedQueries 比运行时查询更频繁。 使用 Criteria API,它的优点是类型安全,因为您可以加入/搜索/添加条件/使用真正的 Java 对象播放它们。

现在关于EntityGraphs:它们只是一个帮助,因此可以从查询中获取其他字段(无论您是使用带有附加属性映射参数的EntityManager.find() 还是Query.setHints())。您还可以将子图用于更复杂的情况。检查this 和this example。

【讨论】:

非常感谢您的回答。澄清一下:使用这种方法,我将不得不根据上下文(以及我想要获取的数据)编写不同的查询?如果我能以更灵活的方式使用实体图,这是我希望避免的。如果那是不可能的,那就太糟糕了。我只是想确保根据使用的上下文可能有多个类似的 JPQL 查询是一个不错的方法。【参考方案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) 或者, 更简单地说,通过使用 QueryDSL 的 fluent API 作为 JPA Criteria API 的替代方案。

使用 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(Predicate predicate) 和 findAll(Precicate predicate) 方法继承自:

http://docs.spring.io/spring-data/commons/docs/current/api/org/springframework/data/querydsl/QueryDslPredicateExecutor.html

除了创建一些接口定义之外,这为您提供了非常少(零)的代码,但仍需要额外的 (在 DB 交互方面次优)处理延迟加载的机制,即您最近的另一篇文章中强调的机制之一 这个问题导致了这个问题。

我不明白为什么不能更新 Spring Data 以处理 EntityGraphs 的动态规范,并且确实存在 围绕这个进行一些讨论,所以它可能正在筹备中:

http://forum.spring.io/forum/spring-projects/data/108202-custom-fetch-groups-with-spring-data-jpa-possible

因此可能值得提出一个 JIRA 以查看他们是否正在计划或询问这是什么

https://***.com/users/18122/oliver-gierke

直接。

Spring Data 已经为新的 JPA 2.1 EntityGraph 功能添加了一些支持b,但是我看不出它可以与我们想要的通用查询方法一起使用:

http://docs.spring.io/spring-data/jpa/docs/1.8.0.M1/reference/html/#jpa.entity-graph

【讨论】:

这有什么更新吗? spring data 是否可以在其标准查询中支持 fetch 计划?

以上是关于使用具有复杂条件的 JPA 实体图的主要内容,如果未能解决你的问题,请参考以下文章

Spring boot 之 使用JPA对数据进行复杂条件的查询

JPA Specification复杂查询及排序

JPA#复杂查询#引子

如何使用条件(where子句)更新实体并在spring数据jpa中的方法响应中获取更新的实体

Spring Data JPA Repository:如何有条件地获取子实体

JPA 2,没有实体映射的连接条件查询