You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Hibernate Criteria含日期条件时查询缓存失效问题排查

Hibernate Query Cache Not Hitting with between Predicate Using new Date()

The Problem

You're working with Hibernate 5.3.12.Final, and you've noticed your query cache works perfectly when you remove the between predicate in your Criteria API query—but misses every time that predicate is included. Here's your query method for reference:

public List<SomeEntity> findArticlesById(String id) {
    final CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
    final CriteriaQuery<SomeEntity> criteriaQuery = criteriaBuilder.createQuery(SomeEntity.class);
    final Root<SomeEntity> root = criteriaQuery.from(SomeEntity.class);
    final Join<SomeEntity, SomeOtherEntity> join = root.join(SomeEntity.property);
    criteriaQuery
        .select(root)
        .where(
            criteriaBuilder.equal(join.get(SomeEntity_.id), id),
            criteriaBuilder.between(criteriaBuilder.literal(new Date()), join.get(SomeEntity_.startDate), join.get(SomeEntity_.endDate))
        );
    return entityManager.createQuery(criteriaQuery)
        .setHint(QueryHints.HINT_CACHEABLE, true)
        .setHint(QueryHints.HINT_CACHE_REGION, CacheRegions.QUERY_ENTITY)
        .getResultList();
}

Your entities are correctly marked @Cacheable with a READ_ONLY concurrency strategy, so the cache itself is configured properly—this is confirmed by the fact that it works when you remove the between clause.

Root Cause

The issue comes down to how Hibernate generates query cache keys. Query cache keys are built from two core components:

  1. The exact structure of your query (the Criteria API logic in this case)
  2. Every parameter value used in the query

In your code, you're using criteriaBuilder.literal(new Date())—every single time you call this method, new Date() creates a fresh Date instance with the current system time (down to the millisecond). Even if you call the method just a few milliseconds apart, the Date value will be different. This means Hibernate generates a completely unique cache key for each call, so it never finds a matching entry in the query cache.

When you remove the between predicate, the only parameter is the id string. Since this value stays the same across calls, the cache key matches, and the query cache hits as expected.

Fixes

Here are two straightforward solutions to get your query cache working again:

1. Use the Database's Current Timestamp

Instead of passing a client-side new Date(), use Hibernate's built-in function to fetch the database's current timestamp. This ensures the value is consistent from the database's perspective, and won't change on every method call:

criteriaBuilder.between(
    criteriaBuilder.currentTimestamp(), 
    join.get(SomeEntity_.startDate), 
    join.get(SomeEntity_.endDate)
);

2. Reuse a Single Date Instance (For Client-Side Time)

If you specifically need to use the application server's current time, extract the Date value once (e.g., at the start of the request or method scope) and reuse it across calls. This guarantees the same value is used, so the cache key remains identical:

// Capture the current time once, outside the query logic
Date currentTime = new Date();

// Use the reused Date instance in your predicate
criteriaBuilder.between(
    criteriaBuilder.literal(currentTime), 
    join.get(SomeEntity_.startDate), 
    join.get(SomeEntity_.endDate)
);

Quick Verification

To confirm this is the root cause, enable Hibernate's cache debug logging (set org.hibernate.cache to DEBUG in your logging config). You'll see that each call with the original between predicate generates a new cache key, while calls without it reuse the same key.

内容的提问来源于stack exchange,提问作者Fabrizio Stellato

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 12:27:41