使用Criteria Builder根据年份筛选数据库完整日期数据
解决Criteria Builder按年份筛选日期字段的问题
核心问题分析
你之前的代码只做了「大于等于当年第一天」的判断,没有限制年份上限,所以会把后续所有年份的数据都包含进来,导致结果超出预期。要精准筛选某一年的所有日期,需要同时限定当年第一天和下一年第一天的范围(或者当年最后一天)。
正确实现代码
以下是两种可靠的实现方式,优先推荐第二种(避免最后一天的时间边界问题,比如数据库字段带时分秒的情况):
if (yearCreated.isPresent()) { int targetYear = Integer.parseInt(yearCreated.get()); LocalDate startOfTargetYear = LocalDate.of(targetYear, 1, 1); LocalDate startOfNextYear = startOfTargetYear.plusYears(1); // 方式1:使用between(适合纯LocalDate字段,无时分秒) predicates.add(criteriaBuilder.between( root.get("yearCreated"), startOfTargetYear, startOfNextYear.minusDays(1) )); // 方式2:组合大于等于+小于(更通用,兼容LocalDateTime字段) // predicates.add(criteriaBuilder.and( // criteriaBuilder.greaterThanOrEqualTo(root.get("yearCreated"), startOfTargetYear), // criteriaBuilder.lessThan(root.get("yearCreated"), startOfNextYear) // )); }
备选方案:提取年份匹配
如果不介意索引失效的风险(日期字段有索引时,函数提取年份会跳过索引),可以直接提取数据库日期的年份进行匹配:
if (yearCreated.isPresent()) { int targetYear = Integer.parseInt(yearCreated.get()); // 注意:不同数据库的年份函数名大多一致(MySQL/Oracle/H2都是YEAR),需根据实际调整 predicates.add(criteriaBuilder.equal( criteriaBuilder.function("YEAR", Integer.class, root.get("yearCreated")), targetYear )); }
为什么之前的代码失效?
你原来的代码调用criteriaBuilder.localDate()的方式有误,而且只设置了下限条件,没有上限,导致查询结果包含目标年份及之后所有年份的数据,自然不符合预期。
内容的提问来源于stack exchange,提问作者Helio Brito
相关产品推荐
相关产品推荐

