使用CriteriaBuilder比较日期时忽略时间部分的实现问题
解决方案
核心问题:
- 数据库存储的
timestamp类型带时分秒,若传入的dateCreation未设置为当日0点,直接比较会漏掉当日早于该时间的记录 - 对数据库字段做函数处理会影响索引命中,优先推荐调整查询参数边界的方案
方案1:调整参数边界(推荐,性能最优)
不需要修改对数据库字段的查询逻辑,只要把传入参数调整为当日零点即可,不会触发字段上的函数计算,可以正常命中索引:
// Java 8+ 版本用LocalDate处理时区问题,转换为当日零点时间戳 LocalDate creationLocalDate = dateCreation.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); Timestamp startOfDay = Timestamp.valueOf(creationLocalDate.atStartOfDay()); // 直接比较即可,所有当日及之后的timestamp都会大于等于当日零点 Predicate predicateDate = criteriaBuilder.greaterThanOrEqualTo(join5.get("dateEvenement"), startOfDay);
如果需要兼容Java 8之前的版本,可以用Calendar手动将时分秒毫秒置为0:
Calendar cal = Calendar.getInstance(); cal.setTime(dateCreation); cal.set(Calendar.HOUR_OF_DAY, 0); cal.set(Calendar.MINUTE, 0); cal.set(Calendar.SECOND, 0); cal.set(Calendar.MILLISECOND, 0); Date startOfDay = cal.getTime(); Predicate predicateDate = criteriaBuilder.greaterThanOrEqualTo(join5.get("dateEvenement"), startOfDay);
方案2:截断数据库字段的时间部分(适配复杂场景)
如果确实需要对数据库存储的timestamp字段去掉时间后再比较,可以用criteriaBuilder.function调用数据库自带的日期截断函数:
- MySQL环境使用
DATE函数:
// 调用DATE函数截断timestamp的时间部分,返回日期类型 Expression<java.sql.Date> eventDate = criteriaBuilder.function("DATE", java.sql.Date.class, join5.get("dateEvenement")); Predicate predicateDate = criteriaBuilder.greaterThanOrEqualTo(eventDate, dateCreation);
- Oracle环境替换函数名为
TRUNC,PostgreSQL环境替换为DATE_TRUNC('day', 字段名),即可适配不同数据库。
内容的提问来源于stack exchange,提问作者jm7757
相关产品推荐
相关产品推荐

