我的 JPA 存储库类中有一个方法,如下所示:
List<Claim> findAllByClaimNumberIn(Set<String> claimNumbers);
如果传入的索赔编号集小于或等于 1000,一切正常。但是,如果传入的索赔编号超过 1000,我会收到错误消息,抱怨“列表中的最大表达式数是 1000"。 有简单的解决方法吗?分页不起作用,因为索赔编号集仍然大于 1000。我能想到的唯一解决方案是将索赔编号集分成 1000 个块并多次查询数据库。 有什么建议吗?
答案 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;
}
}
};
}
}
如果索赔编号的列表大小小于或等于 1000,则一切正常,只有一个谓词就足够了。
否则
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));
}