当前位置: 首页 > 知识库问答 >
问题:

如何解决Spring Data JPA中的N+1问题?

鲁辉
2023-03-14

我使用Spring Data JPA作为持久性层,我面临着N+1问题。我也在使用规范API,因为它我发现很难解决N+1问题。请帮帮忙。

@Entity
public class PopulationHealth {

    @Id
    private int caseId;

    @OneToMany(mappedBy = "caseId", fetch = FetchType.LAZY)
    private List<CostSaving> costSavings;
}
public class CostSaving {

    @Id
    private int caseId;
}
@Transactional(readOnly = true)
public interface PopulationHealthRepository extends JpaRepository<PopulationHealth, Integer>, JpaSpecificationExecutor<PopulationHealth> {

    Page<PopulationHealth> findAll(Specification<PopulationHealth> spec, Pageable pageable);
}
public class PopulationHealthSearchSpec implements Specification<PopulationHealth> {

    private List<PopulationHealthCriteriaDto> criteria;

    public PopulationHealthSearchSpec() {
        criteria = new ArrayList<>();
    }

    public void addCriteria(List<PopulationHealthCriteriaDto> criteria) {
        this.criteria.addAll(criteria);
    }

    @Override
    public Predicate toPredicate(Root<PopulationHealth> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
        List<Predicate> predicates = new ArrayList<>();
        criteria.forEach(p -> {
            SearchOperation operation = p.getOperation();
            root.join("costSavings", JoinType.LEFT);
            switch (operation) {
                case GREATER_THAN:
                    predicates.add(cb.greaterThan(root.get(p.getKey().toString()), convertToDate(p.getValue())));
                    break;
                case GREATER_THAN_EQUAL:
                    predicates.add(cb.greaterThanOrEqualTo(root.get(p.getKey().toString()), convertToDate(p.getValue())));
                    break;
                case LESS_THAN:
                    predicates.add(cb.lessThan(root.get(p.getKey().toString()), convertToDate(p.getValue())));
                    break;
                case LESS_THAN_EQUAL:
                    predicates.add(cb.lessThanOrEqualTo(root.get(p.getKey().toString()), convertToDate(p.getValue())));
                    break;
                case EQUAL:
                    predicates.add(cb.equal(root.get(p.getKey().toString()), p.getValue()));
                    break;
                case NOT_EQUAL:
                    predicates.add(cb.notEqual(root.get(p.getKey().toString()), p.getValue()));
                    break;
                case IN:
                    predicates.add(getInPredicates(cb, root, p));
                    break;
                case NOT_IN:
                    predicates.add(getInPredicates(cb, root, p).not());
                    break;
            }
        });
        return cb.and(predicates.toArray(new Predicate[0]));
    }
2020-05-06 19:46:41,527 DEBUG org.hibernate.SQL : select population0_.CaseId as CaseId1_3_, population0_.CaseCreateDate as CaseCrea2_3_ from POPULATION_HEALTH_UNMASKED population0_ left outer join COST_SAVINGS costsaving1_ on population0_.CaseId=costsaving1_.CaseId where population0_.CaseId in (3098584 , 3098587 , 3098591) order by population0_.CaseCreateDate asc limit ?
2020-05-06 19:46:41,709 DEBUG org.hibernate.SQL : select costsaving0_.CaseId as CaseId1_1_0_, costsaving0_.CaseId as CaseId1_1_1_ from COST_SAVINGS costsaving0_ where costsaving0_.CaseId=?
2020-05-06 19:46:41,744 DEBUG org.hibernate.SQL : select costsaving0_.CaseId as CaseId1_1_0_, costsaving0_.CaseId as CaseId1_1_1_ from COST_SAVINGS costsaving0_ where costsaving0_.CaseId=?
2020-05-06 19:46:41,781 DEBUG org.hibernate.SQL : select costsaving0_.CaseId as CaseId1_1_0_, costsaving0_.CaseId as CaseId1_1_1_ from COST_SAVINGS costsaving0_ where costsaving0_.CaseId=?

在Specification类中使用了fetch()而不是join()之后,我得到了以下问题:

2020-05-06 20:29:25,315 ERROR org.apache.catalina.core.ContainerBase.[Tomcat].[localhost].[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.dao.InvalidDataAccessApiUsageException: org.hibernate.QueryException: query specified join fetching, but the owner of the fetched association was not present in the select list [FromElement{explicit,not a collection join,fetch join,fetch non-lazy properties,classAlias=generatedAlias1,role=com.cambia.mmt.cdqs.api.entity.PopulationHealth.costSavings,tableName=COST_SAVINGS,tableAlias=costsaving1_,origin=POPULATION_HEALTH_UNMASKED population0_,columns={population0_.CaseId ,className=com.cambia.mmt.cdqs.api.entity.CostSaving}}] [select count(generatedAlias0) from com.cambia.mmt.cdqs.api.entity.PopulationHealth as generatedAlias0 left join fetch generatedAlias0.costSavings as generatedAlias1 where generatedAlias0.caseCreateDate>:param0]; nested exception is java.lang.IllegalArgumentException: org.hibernate.QueryException: query specified join fetching, but the owner of the fetched association was not present in the select list [FromElement{explicit,not a collection join,fetch join,fetch non-lazy properties,classAlias=generatedAlias1,role=com.cambia.mmt.cdqs.api.entity.PopulationHealth.costSavings,tableName=COST_SAVINGS,tableAlias=costsaving1_,origin=POPULATION_HEALTH_UNMASKED population0_,columns={population0_.CaseId ,className=com.cambia.mmt.cdqs.api.entity.CostSaving}}] [select count(generatedAlias0) from com.cambia.mmt.cdqs.api.entity.PopulationHealth as generatedAlias0 left join fetch generatedAlias0.costSavings as generatedAlias1 where generatedAlias0.caseCreateDate>:param0]] with root cause
org.hibernate.QueryException: query specified join fetching, but the owner of the fetched association was not present in the select list [FromElement{explicit,not a collection join,fetch join,fetch non-lazy properties,classAlias=generatedAlias1,role=com.cambia.mmt.cdqs.api.entity.PopulationHealth.costSavings,tableName=COST_SAVINGS,tableAlias=costsaving1_,origin=POPULATION_HEALTH_UNMASKED population0_,columns={population0_.CaseId ,className=com.cambia.mmt.cdqs.api.entity.CostSaving}}]

共有1个答案

阳念
2023-03-14

我不认为Spring-Data在这里能做得更好,因为它试图首先执行计数查询,以便在Page对象中提供总计数信息。您可以使用slice来避免计数查询。

如果您想要更高级的东西,可以看看Blaze-Persistence与Spring-Data的集成。它将使用一种不同的分页机制,允许这种工作,也更有效率。使用实体视图甚至会给您带来额外的性能提升。

 类似资料:
  • 问题内容: 我很难理解如何避免在jpa或hibernate状态下进行n + 1选择。 从我阅读的内容来看,有一个“ left join fetch”,但是我不确定它是否仍然适用于多个列表(oneToMany)。 有人可以给我解释一下,还是给我一个带有清晰完整说明的链接? 很抱歉,如果这是一个菜鸟问题,但我找不到关于此问题的真正清晰的文章或文档。 谢谢 问题答案: 除了联接之外,您还可以使用子选择。

  • 问题内容: 我知道N + 1问题是执行一个查询以获取N个记录,执行N个查询以获取一些关系记录。 但是如何在Hibernate中避免这种情况? 问题答案: 假设我们有一个制造商类,与Contact有多对一关系。 我们通过确保初始查询能够获取在适当的初始化状态下加载所需对象所需的所有数据来解决此问题。一种方法是使用HQL提取联接。我们使用HQL 与fetch语句。这导致内部联接: 使用条件查询,我们可

  • 本文向大家介绍Ruby On Rails中如何避免N+1问题,包括了Ruby On Rails中如何避免N+1问题的使用技巧和注意事项,需要的朋友参考一下 N+1问题 N+1问题是数据库访问中最常见的一个性能问题,首先介绍一下什么是N+1问题: 举个例子,我们数据库中有两张表,一个是Customers,一个是Orders。Orders中含有一个外键customer_id,指向了Customers的

  • 抱歉打扰你们了。我知道,这个问题已经问了好几遍了。然而,我就是无法解决我的问题。 所以,我一直在尝试创建一个网球计分系统,当你点击两个按钮之一,赢按钮或输按钮时,就会显示出分数。我有一个按钮,他们可以在那里添加他们的名字到计分系统。当一个选手赢了两盘,比赛就结束了。然后我试着印上他们的名字,上面写着,____赢了比赛。当我尝试使用打印它们名称的变量时,我得到的结果是:[objectHTMLButt

  • 本文向大家介绍用Python解决x的n次方问题,包括了用Python解决x的n次方问题的使用技巧和注意事项,需要的朋友参考一下 我考虑到了x的所有n次的情况,下面的代码有可能是不完美的,但是肯定是对的。 以上这篇用Python解决x的n次方问题就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持呐喊教程。

  • 我有一个颤振按钮的问题,因为按下它不会产生任何事件,我想不出任何方法来修复它。 代码: 代码: 错误: 手势捕获异常处理手势时引发了以下断言:使用不包含导航器的上下文请求的导航器操作。 用于从导航器推送或弹出路由的上下文必须是导航器小部件的后代小部件的上下文。当抛出异常时,这是堆栈:#0 Navigator.of.(包: flutter/src/小部件/navigator.dart:2711: 9