如何使用大型数据集处理 JPA 存储库“findby”

时间:2020-12-22 21:41:41

标签: jpa spring-data-jpa

我的 JPA 存储库类中有一个方法,如下所示:

List<Claim> findAllByClaimNumberIn(Set<String> claimNumbers);

如果传入的索赔编号集小于或等于 1000,一切正常。但是,如果传入的索赔编号超过 1000,我会收到错误消息,抱怨“列表中的最大表达式数是 1000"。 有简单的解决方法吗?分页不起作用,因为索赔编号集仍然大于 1000。我能想到的唯一解决方案是将索赔编号集分成 1000 个块并多次查询数据库。 有什么建议吗?

1 个答案:

答案 0 :(得分:0)

这是一个数据库问题,因此: 一种方法是将原始集合拆分为块,但是:

您不必查询数据库 X 次。相反,您可以以以下形式构建一个“大(动态)查询”:

SELECT claim.*
FROM claim
WHERE
    claim.claimNumber IN (<chunk_1>) OR
    claim.claimNumber IN (<chunk_2>) OR
    -- ...
    claim.claimNumber IN (<chunk_N>)

为此,您可以创建并使用规范(请参阅https://spring.io/blog/2011/04/26/advanced-spring-data-jpa-specifications-and-querydsl/)来动态构建查询:

(我将 Set 更改为 List 以便于对列表进行分区)

public class ClaimSpecs {

  public static Specification<Claim> claimNumberSpec(List<String> claimNumbers) {
    return new Specification<Claim>() {

      @Override
      public Predicate toPredicate(Root<Claim> root, CriteriaQuery<?> query,
          CriteriaBuilder criteriaBuilder) {
        if (claimNumbers.size() <= 1000) {
          // 1.
          return root.get("claimNumber").in(claimNumbers);
        } else {
          // 2.1.
          final int CHUNK_SIZE = 1000;
          List<List<String>> chunks = new ArrayList<>();
          for (int i = 0; i < claimNumbers.size(); i += CHUNK_SIZE) {
            chunks.add(claimNumbers.subList(i, Math.min(claimNumbers.size(), i + CHUNK_SIZE)));
          }
          // 2.2.
          Predicate predicate = criteriaBuilder.conjunction();
          for (List<String> chunk : chunks) {
            predicate = criteriaBuilder.or(predicate, root.get("claimNumber").in(chunk));
          }
          return predicate;
        }
      }
    };
  }

}
  1. 如果索赔编号的列表大小小于或等于 1000,则一切正常,只有一个谓词就足够了。

  2. 否则

2.1.索赔编号列表必须划分为大小为 1000(或更小)的子列表(取自 https://stackoverflow.com/a/2895365/2201165)和

2.2.必须根据创建的块创建多个谓词,并用 OR 连接。


要使用此规范(作为存储库方法中的参数),您的存储库必须扩展 JpaSpecificationExecutor 并如下所示:

public interface ClaimRepository
    extends JpaRepository<Claim, Long>, JpaSpecificationExecutor<Claim> {

}

鉴于此,您可以像这样调用此存储库:

@GetMapping                                                                 
public List<Claim> findAllClaimsByClaimNumberIn(List<String> claimNumbers) {
  return claimRepository.findAll(ClaimSpecs.claimNumberSpec(claimNumbers)); 
}                                                                           
相关问题