How do you control the maximum size and expiry of one individual Hibernate second-level cache region, and how do you work out which region name corresponds to a given entity or collection?
answer
- Region name = FQCN of root entity; collection = FQCN.field
- @Cache(region=…) renames; region_prefix namespaces
- Sizing/TTL live in provider config keyed by region name
- default-query-results-region / default-update-timestamps-region
- Watch hits vs puts vs evictions per region
basics
~20 sRegions default to the fully-qualified entity name; collection regions append the field name. Override with @Cache(region="..."), prefix all with hibernate.cache.region_prefix. Size and TTL are set per region in the provider's config file, not by Hibernate.
solid answer
~50 s**Naming.** By default an entity region is the fully-qualified class name of the root entity (`com.acme.shop.Product`); a collection region is that plus the field (`com.acme.shop.Product.tags`); natural-id regions get their own suffix. `@Cache(region = "products")` overrides the name, letting several entities share one region, and `hibernate.cache.region_prefix` prefixes everything — essential when multiple applications share one cache manager. **Sizing and expiry.** Hibernate exposes no TTL or max-entries knob. With the JCache bridge it just calls `cacheManager.getCache(regionName)`; capacity, eviction policy, tiering and expiry are declared in the provider's own configuration keyed by that exact name. Get the name wrong and, with the default `missing_cache_strategy=fail`, startup breaks — which is far better than silently caching into a default region. Size regions from measured entry size × desired residency, then watch per-region hit ratio and eviction rate. TTL is a staleness budget, not a performance knob: expiry causes a miss and a database read.
code
java · 15 lines@Entity
@Cacheable
@Cache(usage = CacheConcurrencyStrategy.READ_ONLY, region = "reference-data")
public class Country { @Id private String iso; }
@Entity
@Cacheable
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE) // region = com.acme.shop.Product
public class Product {
@Id private Long id;
@OneToMany(mappedBy = "product")
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE) // region = com.acme.shop.Product.tags
private Set<Tag> tags;
}go deeper
Know that regions are named after the entity class and that sizing is configured in the cache provider, not Hibernate.
Add collection region naming, @Cache(region=…), the region prefix, and why a name mismatch fails startup.
Reason about sizing from working-set and entry cost, and diagnose regions using per-region hit/put/eviction statistics.
Treat TTL as a staleness budget tied to who else writes the data, and decide when the honest answer is to shrink what is cached rather than grow the region.
## Region names are the join key Everything about sizing hinges on names, because Hibernate and the cache provider agree on nothing else. Hibernate computes a region name, asks the provider for a cache with that name, and uses it. If the provider's configuration file uses a different string, the two never meet. The defaults: - **Entity region**: the fully-qualified entity name — `com.acme.shop.Product`. For an inheritance hierarchy, the **root** entity's name is used, so subclasses share one region. - **Collection region**: fully-qualified owner plus the attribute — `com.acme.shop.Product.tags`. A collection is a separate region from its owning entity and needs its own `@Cache` annotation to be cached at all. - **Natural-id region**: derived similarly with a natural-id suffix. - **Query results region**: `default-query-results-region` unless a query names its own. - **Update timestamps region**: `default-update-timestamps-region`. Two knobs change names. `@Cache(region = "reference-data")` sets an explicit name, and several entities may point at the same one — useful for dozens of tiny lookup tables that would otherwise need dozens of configured caches, at the cost of losing per-type hit statistics and shared eviction pressure. `hibernate.cache.region_prefix=orders-svc` prefixes every region name; without it, two applications sharing one `CacheManager` would collide on identically named entity classes. ## Where sizing actually lives Hibernate deliberately does not abstract capacity or expiry. With `hibernate-jcache`, region configuration is written in the provider's dialect: - EhCache 3: `<cache alias="com.acme.shop.Product">` with `<resources><heap unit="entries">…</heap></resources>` and `<expiry><ttl …/></expiry>`, optionally offheap or disk tiers. - Caffeine's JCache module: per-cache specs with maximum size and expire-after-write/access. - Infinispan: per-cache XML with eviction, expiration and clustering mode. Because Hibernate has no opinion, a missing declaration is a real risk — hence `hibernate.javax.cache.missing_cache_strategy`, which defaults to `fail`. Failing at startup on a typo'd region name is the desired behaviour; the alternative is an unbounded auto-created region quietly filling the heap. ## Choosing numbers *Max entries.* Start from what you want resident. For a lookup table of 800 rows that is fully cacheable, size 1000 and be done. For a 50-million-row table, caching by id is only useful if access is strongly skewed; size to the hot working set, and accept the rest as misses. A region that evicts constantly is worse than no region: you pay puts and evictions and get almost no hits. *Entry cost.* Entries are dehydrated state arrays, roughly the columns plus overhead — not object graphs, but a wide row or a `@Lob` field is still heavy. Multiply measured entry size by max entries before deciding whether it belongs on heap, offheap, or nowhere. *TTL.* Expiry is a **staleness budget** for anything Hibernate cannot see: batch jobs, other services, bulk HQL `update`/`delete`, direct SQL. Within one JVM, Hibernate maintains the region on write, so a short TTL is not needed for correctness there — it is needed to bound divergence from outside writes and from other nodes. Setting TTL to seconds “to be safe” mostly converts hits into misses; if you truly need second-level freshness, question whether the entity should be cached at all. ## Verifying Enable `hibernate.generate_statistics=true` and read per-region counters from `SessionFactory.getStatistics()`: hit count, miss count, put count and element count in memory. The diagnostic patterns: - **High puts, low hits** — the region is not earning its memory; the access path may not be id-based, or the data is written more than read. - **High evictions with a decent hit ratio** — too small; increase max entries and watch heap. - **Zero puts** — the entity is not actually enrolled: check `@Cacheable`, the shared-cache mode, or a region-name mismatch. Export those alongside the provider's own metrics; the two views disagreeing is itself a signal (for example, Hibernate putting into a region the provider is evicting instantly because the configured size is a handful of entries).
- You point several small lookup entities at one shared region name. What do you gain and what do you lose?You gain a single cache to declare, size and monitor instead of one per tiny table, which keeps provider configuration manageable. You lose per-entity hit statistics, so a badly behaving type hides inside the aggregate, and all the entities now compete for the same eviction budget — a burst on one can evict another's entries. It is a reasonable trade for genuinely tiny, read-only reference data, and a poor one otherwise.
- Would you set a short TTL on an entity region to protect against stale data?Only to bound divergence from writes Hibernate cannot observe — batch jobs, other services, bulk HQL updates, or other nodes with local caches. Within one JVM, Hibernate maintains the region on write, so TTL adds nothing for correctness there and mostly converts hits into database reads. If the freshness requirement is measured in seconds, that entity probably should not be cached.
saying these in an interview costs you the question
- Expecting hibernate.* properties for max size or TTL
- Assuming the collection is cached because the owning entity is @Cacheable
- Using the subclass name as the region for an inheritance hierarchy
- Setting aggressive TTLs and calling it a performance tuning step
- Sharing a CacheManager across applications without a region prefix