Skip to content
AITroveRead. Build. Understand.
Make this comfortable

Spring null-result caching: decide whether a miss should survive

Last updated: 5 Oct 20264 min read
tutorial
IntermediateBy AITrove Editorial

A missing record can be deliberately uncached or cached briefly; either choice changes load and freshness after creation.

A miss is an outcome

A receipt may be absent because it was never created, because replication has not caught up, or because the caller cannot see it. These cases should not automatically share one cache key or retention policy. With @Cacheable, an unless expression can veto storage after the method returns; the example does not cache null. Tenant-aware keys prevent one tenant's miss from masking another tenant's receipt. Do not encode authorization failure as a cached absence.

Choose the load tradeoff

Never caching misses preserves immediate visibility when a receipt is created, but repeated requests for unknown IDs all reach the database. A short negative cache can protect the database under repeated bad lookups, but a newly created record remains invisible until invalidation or expiry. If the result type is Optional, Spring evaluates #result against its contained value, so an empty Optional is still a null business result for this expression. Check what the selected provider stores when nulls are allowed.

Exercise creation after absence

Query an absent receipt, create it, and query again through the same API. With a no-null policy, the second read should reach storage and return it. For negative caching, add an explicit create-time invalidation and assert its behavior across nodes and rollback. Count misses by bounded route and outcome, never by receipt ID. Commit timing matters for that invalidation, and expiry limits the damage when a signal is lost.

Implementation contract

Java
@Cacheable(cacheNames = "receipt-view-v3",
           key = "#p0 + ':' + #p1",
           unless = "#result == null")
public ReceiptView findVisibleReceipt(String tenantId, String receiptId) {
    return receipts.findVisible(tenantId, receiptId).orElse(null);
}

Cost and verification

Skipping null storage increases repeated database work for missing IDs. Negative caching reduces that work but spends capacity on misses and adds a freshness window that must be accepted by the product contract.

Common Mistakes

  • Do not share a missing-result key across tenants or authorization scopes.
  • Do not cache an empty result indefinitely when the record may be created later.
  • Do not use a null cache entry to hide an authorization failure.

Read next

Spring cache keys: separate tenants and test the loader count, Spring cache after commit: keep rolled-back writes out of readers, Spring Redis cache TTL versus idle expiry: GETEX changes the read contract, Spring cache eviction: invalidate the same tenant key used by the read, Spring @Cacheable sync: collapse a local same-key cache miss.

spring
spring-boot
production
cache-null-result-policy
Storage details