Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.

Java Caching Essentials is DZone Refcard #216, a six-page, JCache-focused reference authored by Granville Barnett, an architect at Hazelcast. It explains cache fundamentals, JSR 107 APIs, expiration, events, entry processing, integration hooks and deployment models. It is a useful starting point—not a complete production architecture guide—and its implementation discussion should be read with its Hazelcast-informed perspective in mind.

The practical decision is whether your application needs a standardized javax.cache API, a framework abstraction such as Spring Cache, a local library such as Caffeine or Ehcache, or a remote system such as Redis or Hazelcast. The right choice depends on freshness, topology, workload and operations, not on the existence of a standard alone.

What a cache does—and what it cannot guarantee

A cache stores a previously retrieved or computed result under a key. A hit returns a usable value without repeating the underlying work. A miss forces the application to query a database, call a service or perform the computation, after which it may populate the cache.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Caching helps only when checking the cache costs less than the work avoided. It can reduce latency, database load and network traffic, but it introduces a second copy of data whose freshness and failure behavior must be designed.

Good candidates

  • Frequently read values with deterministic keys.
  • Expensive database queries, network calls, page renders or computations.
  • Data that remains valid for a defined period or can be explicitly invalidated.
  • Values small enough for the selected memory and storage budget.
  • Results that can safely be served slightly stale.

Poor candidates

  • Highly volatile values with strict real-time freshness requirements.
  • Large, rarely reused objects.
  • Secrets or personal data without isolation, encryption and access controls.
  • Results whose invalidation rules are unknown.
  • Values whose key omits tenant, locale, permissions, API version or another correctness input.

What the DZone Refcard actually covers

DZone’s page at https://dzone.com/refcardz/java-caching organizes the material into an introduction, caching overview, JCache essentials and conclusion. The examples cover hits and misses, session state, database results, rendered pages, provider discovery, cache managers, configuration, expiry, listeners, entry processors, JMX statistics, read-through and write-through integration, and embedded, client-server and hybrid deployments.

Hazelcast’s companion page describes the Refcard as six pages and emphasizes latency reduction, operating-cost reduction, provider independence, expiry, events, entry processors and JMX management: https://hazelcast.com/resources/dzone-java-caching-essentials/. Because the author works at Hazelcast, comparisons involving implementation choices are not a neutral survey of every cache product.

JCache and JSR 107: an API, not an engine

JCache is the standardized Java caching API defined by JSR 107. Applications use common interfaces while a provider supplies storage, serialization, topology, eviction and operational behavior. Hazelcast documents the API and its provider model at https://docs.hazelcast.com/hazelcast/5.7/jcache/overview; Ehcache documents its JSR-107 integration at https://www.ehcache.org/documentation/3.9/107.html.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Standard interfaces can reduce application-code coupling, but portability is not free. Provider configuration, serialization formats, expiry timing, clustering, consistency, transactions, monitoring and failure handling can still require migration work. JCache’s main namespace is javax.cache, not jakarta.cache. Verify Java-runtime, provider, application-server and framework compatibility before adopting examples in a Jakarta-based stack. JCache 1.0.0 was released in March 2014, 1.1.0 in December 2017 and 1.1.1 in May 2019; Hazelcast describes 1.1.x as backward-compatible clarifications and fixes rather than a new feature generation.

The core objects

  • Caching: entry point for obtaining a provider.
  • CachingProvider: implementation selected through the JCache service-provider mechanism.
  • CacheManager: creates, finds and destroys named caches.
  • Cache<K,V>: map-like operations such as get, put, remove and invoke.
  • Configuration and MutableConfiguration: key/value types and behavior.
  • ExpiryPolicy: rules for when entries become unavailable.
  • CacheEntryListener: notifications for creation, update, expiry and removal.
  • EntryProcessor: cache-aware processing of an entry.
  • Management and statistics interfaces: operational visibility, commonly exposed through JMX.

A minimal provider-backed JCache example

The API and a provider implementation must both be present at runtime. Ehcache’s documentation explicitly calls out the need for the JCache API JAR and provider JAR. The following flow follows the Refcard’s basic example:

import java.util.Map;
import javax.cache.Cache;
import javax.cache.CacheManager;
import javax.cache.Caching;
import javax.cache.configuration.MutableConfiguration;
import javax.cache.spi.CachingProvider;

public class App {
    public static void main(String[] args) {
        CachingProvider provider = Caching.getCachingProvider();
        CacheManager manager = provider.getCacheManager();

        MutableConfiguration<String, String> config =
            new MutableConfiguration<>();
        Cache<String, String> cache =
            manager.createCache("dzone-cache", config);

        cache.put("England", "London");
        cache.putAll(Map.of("France", "Paris", "Ireland", "Dublin"));

        assert cache.get("England").equals("London");
        assert cache.get("Italy") == null;
    }
}
  1. Obtain a CachingProvider.
  2. Obtain its CacheManager.
  3. Build a configuration.
  4. Create a named cache.
  5. Write and read entries, handling absent keys.

Make provider selection deterministic

Caching.getCachingProvider() uses service-provider discovery. A provider registers its implementation through META-INF/services/javax.cache.spi.CachingProvider. With more than one provider on the classpath, the unqualified lookup can throw CacheException. Use the overload that names the implementation class when multiple providers, test and production implementations, class loaders or plugins are involved. Keep the selected provider explicit in deployment and integration tests.

Configuration, expiry and capacity

Types and storage semantics

Declare key and value types where possible. Ehcache’s example also sets store-by-reference explicitly:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
MutableConfiguration<Long, String> configuration =
    new MutableConfiguration<Long, String>()
        .setTypes(Long.class, String.class)
        .setStoreByValue(false)
        .setExpiryPolicyFactory(
            CreatedExpiryPolicy.factoryOf(Duration.ONE_MINUTE)
        );

Store-by-value, described as the default mechanism in Hazelcast’s JCache overview, isolates cached objects through copying or serialization but can add CPU and memory cost. Store-by-reference can be faster for local objects, yet mutable values can change outside cache operations and create surprising behavior.

Expiry is not invalidation

A created-expiry policy removes an entry a fixed time after creation:

cacheConfig.setExpiryPolicyFactory(
    CreatedExpiryPolicy.factoryOf(Duration.ONE_DAY)
);

Created expiry differs from expire-after-access, expire-after-update, sliding or maximum-idle policies. None replaces invalidation after a source-of-truth write. Choose an explicit rule for database updates, event-driven invalidation, remove, versioned keys and tenant-specific changes.

Bound the cache

The introductory example has no capacity limit. Production configuration needs a maximum entry count or memory budget, an eviction policy and an estimate of object, index and serialization overhead. Monitor heap pressure, garbage collection and behavior when the cache is full; an unbounded cache can become an outage mechanism.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Events and entry processing

Listeners

JCache listeners can receive creation, update, expiry and removal events through MutableCacheEntryListenerConfiguration. They are useful for metrics, diagnostics, secondary-index maintenance and controlled invalidation propagation.

Listeners are not a durable event stream. Define behavior for retries, duplicate delivery, recursion, ordering and listener failure. A synchronous listener can increase request latency; an asynchronous one needs its own queue, back-pressure and loss policy.

Entry processors

class AppendUuidEntryProcessor
        implements EntryProcessor<String, String, String> {
    @Override
    public String process(MutableEntry<String, String> entry,
                          Object... arguments)
            throws EntryProcessorException {
        if (!entry.exists()) return null;
        String value = entry.getValue() + "-" + UUID.randomUUID();
        entry.setValue(value);
        return value;
    }
}

cache.invoke(key, new AppendUuidEntryProcessor());

An entry processor can avoid a client-side read-modify-write sequence and, in a distributed provider, may execute close to the data. Its guarantees remain provider-specific: it is not automatically a business transaction or a universal linearizability promise.

Read-through, write-through and cache-aside

JCache’s integration package provides CacheLoader for loading missing values and CacheWriter for propagating mutations. Read-through can centralize loading, but mass expiry can overload a database. Write-through makes a cache write synchronously dependent on the backing store, and failures must specify whether the operation fails, retries or leaves partial state. It is not automatically a durable transaction across two systems.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Cache-aside—application code reads the store on a miss, populates the cache and explicitly invalidates after writes—is often easier to reason about when consistency rules are complex. Select the pattern only after defining failure, retry and freshness behavior.

Deployment models

Model Strengths Costs and risks Best fit
Embedded Very low latency, no network hop, simple single-process deployment Each instance has different data; uses application heap; restart loss and rebuild storms Instance-local, rebuildable data where minimum latency matters
Client-server Shared state, independent scaling, centralized capacity and replication Network failures, serialization overhead and a separate availability/security design Several instances need shared data or independently scalable capacity
Hybrid/near-cache Local speed for hot values plus shared remote capacity Duplicate memory, harder invalidation, more complex observability and freshness A small hot set justifies complexity and the provider supports reliable invalidation

Hazelcast discusses these deployment choices at https://docs.hazelcast.com/hazelcast/5.7/jcache/overview. A distributed cache is not inherently resilient: replication, partition handling, failover, capacity and recovery must be designed and tested.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Production failure modes and defenses

Stale or incorrectly scoped data

Write down maximum tolerated age, invalidating writes, whether stale data is allowed during backend failure and which identity or authorization dimensions belong in the key. Never share a value across tenants or permission contexts accidentally.

Cache stampede

When a popular key expires, concurrent requests can all refill it. Use per-key locking or request coalescing, bounded backend concurrency, early refresh, randomized TTL jitter, stale-while-revalidate or warm-up for predictable hot keys.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Cache penetration

Repeated requests for nonexistent records can hit the database forever. Consider short-lived negative caching, input validation, Bloom filters where appropriate and abuse controls. Ensure a cached “not found” result does not reveal authorization-sensitive information.

Hot keys

A single key can bottleneck despite healthy aggregate throughput. Depending on semantics, use local copies, replication or key diversification, and avoid one global lock.

Serialization, upgrades and security

Distributed values need stable serialization across rolling upgrades and class-loader boundaries. Control payload size, protect sensitive data and review deserialization security. Test backward and forward compatibility before changing classes in a live cluster.

Cache outages

Decide whether the application fails open to the database, fails closed, serves stale data or trips a circuit breaker. Apply backend rate limits and define warm-up and recovery procedures so a cold cache does not become a database outage.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

What to monitor

JMX can expose configuration, hit and miss percentages and average get and put times. Those are starting points, not a complete service-level view. Monitor:

  • Hit, miss and backend-fetch rates by cache.
  • Load latency, errors, eviction and expiry counts.
  • Entry count, estimated memory, serialization time and garbage-collection impact.
  • Refresh or invalidation lag, stampede indicators and hot-key skew.
  • Request latency, database load and freshness by tenant or region.

A high hit rate can coexist with unacceptable staleness, memory pressure or hot-key contention.

Choosing JCache, a provider or another abstraction

Option Strength Use it when Watch for
Caffeine High-performance local in-process cache Single-process caching, especially with Spring Cache No shared cross-instance state or remote capacity
Ehcache Embedded cache with JSR-107 integration Local caching and a JCache-compatible API Not a substitute for a managed, multi-region service
Hazelcast Distributed data grid with JCache support Shared state, clustering, near-cache or enterprise operations Additional topology and operational complexity; verify current product support at https://docs.hazelcast.com/hazelcast/5.7/jcache/tck
Redis Remote key-value service with broad language support An existing or managed remote cache is preferred Redis is not automatically a JCache provider; verify adapters, serialization and guarantees
Spring Cache Framework-level annotations and backend flexibility A Spring application wants a common caching abstraction It does not expose every native distributed-cache feature
Direct vendor API Full provider-specific features Topology, async, reactive, scripting or data structures matter Greater provider coupling

Relevant official references are Caffeine, its JCache guidance, Ehcache, Redis, Redis pricing, Spring Cache, and Hazelcast’s pricing page. Current prices, limits, regions and support terms should be verified directly; no numeric price is established here.

A practical selection checklist

  • Define freshness, invalidation and stale-read requirements.
  • Measure expected hit rate, object size, backend cost and latency budget.
  • Choose embedded, client-server or hybrid topology.
  • Set memory limits, eviction and expiry before production.
  • Design keys for tenant, locale, permissions and version where required.
  • Plan stampede, penetration, hot-key and outage behavior.
  • Verify javax.cache compatibility with the Java and Jakarta stack.
  • Make provider selection explicit and test provider-specific semantics.
  • Instrument cache and backend metrics together.

Verdict

JCache is valuable when a common API and source-level provider flexibility matter, and when the required features fit JSR 107. It is not a cache server, a freshness policy or a guarantee of operational portability. For a small local cache, Caffeine or Ehcache may be simpler; a Spring application may start with Spring Cache; shared remote state may point to Hazelcast or Redis. Choose after specifying consistency, topology, capacity, recovery and framework compatibility—not merely because JCache is standardized.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.