Hibernate Ehcache configuration is the ehcache.xml file that gives every Hibernate cache region its own Ehcache cache, with a size limit and an expiry time. Hibernate decides what goes into the second level cache and names a region for each cached entity, collection and query. Ehcache 3 stores the entries, and ehcache.xml sets the size limit and the expiry of each region.
We use ehcache.xml to keep the cache of a Hibernate app inside a known memory budget and to limit how old a cached value can get. It matters most for reference data such as languages or countries, which many requests read and few requests change.
The following example declares a shared template, one entity region and the timestamps region, and then points Hibernate to the file and reads the statistics of two regions.
<config xmlns="http://www.ehcache.org/v3">
<cache-template name="reference-data">
<expiry>
<ttl unit="minutes">30</ttl>
</expiry>
<heap unit="entries">500</heap>
</cache-template>
<cache alias="com.howtodoinjava.hibernate.ehcacheconfig.Language" uses-template="reference-data"/>
<cache alias="default-update-timestamps-region">
<expiry>
<none/>
</expiry>
<heap unit="entries">100</heap>
</cache>
</config>
.property("hibernate.javax.cache.uri", "ehcache.xml") // read from the classpath
Statistics stats = emf.unwrap(SessionFactory.class).getStatistics();
long hits = stats.getDomainDataRegionStatistics(Language.class.getName()).getHitCount(); // 2
long misses = stats.getQueryRegionStatistics("language-queries").getMissCount(); // 1
Notice that the cache alias equals the region name character for character. When the alias does not match, Hibernate creates a separate cache without any limit, and the settings in the file are never used.
Next, we see how Hibernate names its regions and build the file step by step. After that, we cover templates, ttl and tti expiry, heap sizing, offheap storage, missing regions, concurrency strategies and per-region statistics.
1. How Hibernate Names Its Cache Regions
Hibernate 6 and later use Ehcache 3 through the JCache API. In short, we add hibernate-jcache and ehcache (classifier jakarta), and set four properties.
.property("hibernate.cache.region.factory_class", "jcache")
.property("hibernate.javax.cache.provider", "org.ehcache.jsr107.EhcacheCachingProvider")
.property("hibernate.javax.cache.uri", "ehcache.xml")
.property("hibernate.cache.use_query_cache", true)
.property("hibernate.generate_statistics", true)
A region is a named cache that Hibernate asks the provider for. Hibernate builds one region for each cached entity class and for each cached collection, plus two regions for the query cache. Ehcache looks up a <cache> element whose alias equals the region name, and in Hibernate 7.4 the default names come from the class and field names.
| What we cache | Default region name | Changed with |
|---|---|---|
| Entity Language | com.howtodoinjava.hibernate.ehcacheconfig.Language (the class name) | @Cache(region = “…”) |
| Collection Language.translations | com.howtodoinjava.hibernate.ehcacheconfig.Language.translations (class name + field) | @Cache(region = “…”) on the field |
| Query results with HINT_CACHEABLE | default-query-results-region | HINT_CACHE_REGION on the query |
| Last change time of each table | default-update-timestamps-region | Cannot be renamed |
Our example is the translation store of an app, where a Language (“en”, “English”) has many Translation rows such as greeting = Hello. Every page of the app reads these texts, but a translator changes them only now and then. The language keeps the default region name, whereas the translation entity moves to a short region called translations.
@Entity
@Cacheable
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class Language {
@Id
@GeneratedValue
private Long id;
private String code;
private String name;
@OneToMany(mappedBy = "language")
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
private List<Translation> translations = new ArrayList<>();
}
@Entity
@Cacheable
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE, region = "translations")
public class Translation {
@Id
@GeneratedValue
private Long id;
@Column(name = "message_key")
private String key;
private String text;
@ManyToOne(fetch = FetchType.LAZY)
private Language language;
}
The @OneToMany field needs its own @Cache, because caching the class does not cache its collections. Hibernate stores each region from the code in the cache with the same alias in ehcache.xml.

2. Ehcache Configuration Example for Hibernate 7
The example runs on Java 25, Hibernate 7.4.11.Final, Ehcache 3.12.0 and an in-memory H2 database. The complete project on GitHub prints the settings Ehcache applied to every cache along with the SQL and statistics of each step, and 22 JUnit tests check all of them (mvn compile exec:java, mvn test).
2.1. The Structure of ehcache.xml
One <config> element describes one Ehcache CacheManager. Hibernate creates that manager from the file when the EntityManagerFactory starts and closes it with the factory. Inside <config>, the file needs only a few elements.
| Element | Purpose |
|---|---|
| <cache-template name=”…”> | Settings shared by several caches |
| <cache alias=”…”> | One cache; the alias is the Hibernate region name |
| uses-template=”…” | Copies all settings of a template into the cache |
| <expiry> | <ttl>, <tti> or <none/> |
| <heap unit=”entries”> | Size of the on-heap store, in entries or in kB/MB |
| <resources> | Several storage tiers together: <heap>, <offheap>, <disk> |
| <key-type>, <value-type> | Type check on keys and values; leave them out for Hibernate (see FAQ 3.2) |
The complete file of the example declares every region from the table in section 1, plus the named query region language-queries.
<config xmlns="http://www.ehcache.org/v3">
<!-- Shared settings for reference data -->
<cache-template name="reference-data">
<expiry>
<ttl unit="minutes">30</ttl>
</expiry>
<heap unit="entries">500</heap>
</cache-template>
<!-- Entity region: the fully qualified class name -->
<cache alias="com.howtodoinjava.hibernate.ehcacheconfig.Language" uses-template="reference-data"/>
<!-- Collection region: class name + "." + field name -->
<cache alias="com.howtodoinjava.hibernate.ehcacheconfig.Language.translations" uses-template="reference-data"/>
<!-- Entity region renamed with @Cache(region = "translations"); overrides the template -->
<cache alias="translations" uses-template="reference-data">
<expiry>
<tti unit="minutes">10</tti>
</expiry>
<heap unit="entries">5000</heap>
</cache>
<!-- Query results: the default region and a named one -->
<cache alias="default-query-results-region">
<expiry>
<ttl unit="minutes">5</ttl>
</expiry>
<heap unit="entries">200</heap>
</cache>
<cache alias="language-queries" uses-template="reference-data"/>
<!-- Last change time per table: never expire -->
<cache alias="default-update-timestamps-region">
<expiry>
<none/>
</expiry>
<heap unit="entries">100</heap>
</cache>
</config>
A wrong alias does not fail. Hibernate creates a separate cache, and the settings in the file are never used. For example, when a refactoring moves Language to another package, the region name changes with it, and the old alias stays in the file without any effect. So we always read the applied settings back from Ehcache once, through the JCache CacheManager that Hibernate created.
CacheManager cacheManager = ((JCacheRegionFactory) emf.unwrap(SessionFactoryImplementor.class)
.getCache().getRegionFactory()).getCacheManager();
Eh107Configuration<Object, Object> config = cacheManager.getCache("translations").getConfiguration(Eh107Configuration.class);
CacheRuntimeConfiguration<Object, Object> runtime = config.unwrap(CacheRuntimeConfiguration.class);
ResourcePools pools = runtime.getResourcePools(); // heap=5000 entries
ExpiryPolicy<?, ?> expiry = runtime.getExpiryPolicy(); // TTI of PT10M
com.howtodoinjava.hibernate.ehcacheconfig.Language -> heap=500 entries, TTL of PT30M
com.howtodoinjava.hibernate.ehcacheconfig.Language.translations -> heap=500 entries, TTL of PT30M
default-query-results-region -> heap=200 entries, TTL of PT5M
default-update-timestamps-region -> heap=100 entries, No Expiry
language-queries -> heap=500 entries, TTL of PT30M
translations -> heap=5000 entries, TTI of PT10M
2.2. Sharing Settings With Cache Templates
Most regions of reference data need the same limits. A <cache-template> holds them once, and each cache picks them up with uses-template. Elements written inside the cache override the template. For example, translations takes the template but replaces its ttl with a tti and its heap with 5000 entries, as the last line of the output in section 2.1 shows.
A template is not a fallback for regions we forgot, so a region without a <cache> element does not use any template unless we set the JCache default template from section 2.6.
2.3. Expiry With ttl, tti and none
Expiry removes an entry after some time, so the cache picks up changes that other programs made in the database. For example, a support engineer fixes a French translation with a SQL script, and Hibernate never sees that update. With a 30 minute ttl, the app shows the old text for at most 30 minutes. Ehcache 3 has three expiry types, and each one starts the clock at a different moment.
| Element | The entry expires | Good for |
|---|---|---|
| <ttl unit=”minutes”>30</ttl> | 30 minutes after it was stored, even if it is read all the time | Data that other programs can change |
| <tti unit=”minutes”>10</tti> | 10 minutes after the last read | Keeping hot entries and dropping unused ones |
| <none/> | Never; only eviction or our code removes it | default-update-timestamps-region |
To test expiry, the file ehcache-expiry.xml sets a 1 second ttl on Language and a 2 second tti on translations. The pause() helper waits with LockSupport.parkNanos(), and each find() runs in a new session.
<cache alias="com.howtodoinjava.hibernate.ehcacheconfig.Language">
<expiry>
<ttl unit="seconds">1</ttl>
</expiry>
<heap unit="entries">100</heap>
</cache>
<cache alias="translations">
<expiry>
<tti unit="seconds">2</tti>
</expiry>
<heap unit="entries">100</heap>
</cache>
// the insert stored English at t=0
emf.runInTransaction(em -> em.find(Language.class, enId)); // t=1ms: hit
pause(Duration.ofMillis(500));
emf.runInTransaction(em -> em.find(Language.class, enId)); // t=508ms: hit
pause(Duration.ofMillis(700));
emf.runInTransaction(em -> em.find(Language.class, enId)); // t=1213ms: miss
// select l1_0.id,l1_0.code,l1_0.name from Language l1_0 where l1_0.id=?
emf.runInTransaction(em -> em.find(Translation.class, helloId)); // t=6ms: miss, put
pause(Duration.ofMillis(1200));
emf.runInTransaction(em -> em.find(Translation.class, helloId)); // t=1208ms: hit
pause(Duration.ofMillis(1200));
emf.runInTransaction(em -> em.find(Translation.class, helloId)); // t=2410ms: hit, older than 2 s
pause(Duration.ofMillis(2200));
emf.runInTransaction(em -> em.find(Translation.class, helloId)); // t=4613ms: miss
// select t1_0.id,t1_0.message_key,t1_0.language_id,t1_0.text from Translation t1_0 where t1_0.id=?
The third translation read is a hit although the entry is 2.4 seconds old, because each read restarts the idle time.

A tti alone never refreshes an entry that is read often, so a value changed by another program can stay stale for as long as people keep reading it. For data that other programs change, we use ttl.
2.4. Sizing the Heap by Entries or by Memory
The heap tier keeps the cached values as Java objects in the JVM heap, and we size it in one of two ways.
- <heap unit=”entries”>500</heap> counts entries, which is fast and predictable.
- <heap unit=”kB”>64</heap> (or MB) measures the memory of each entry, so Ehcache walks the object graph of every value to compute its size.
Byte sizing has a cost on every put, and on Java 25 the module system blocks part of the object walk. We stored 1,000 languages in a 64 kB heap and got a warning with a stack trace.
WARN org.ehcache.sizeof.ObjectGraphWalker - The JVM is preventing Ehcache from accessing the subgraph beneath 'private final byte[] java.lang.String.value' - cache sizes may be underestimated as a result
java.lang.reflect.InaccessibleObjectException: Unable to make field private final byte[] java.lang.String.value accessible: module java.base does not "opens java.lang" to unnamed module @37e4624e
Language entries in 64 kB: 154 of 1000
We size the heap in entries for Hibernate regions. The size estimate skips the contents of every String, so a memory limit is not reliable, and the log gets a warning with a stack trace.
2.5. Eviction and the Offheap Tier
When the heap is full, Ehcache removes an entry to make room for a new one. This is called eviction, and the evicted entry is gone from the cache, so the next read of it goes to the database. The file ehcache-small-heap.xml gives the Language region a heap of 2 entries, and we store three languages.
<!-- Room for two languages only -->
<cache alias="com.howtodoinjava.hibernate.ehcacheconfig.Language">
<heap unit="entries">2</heap>
</cache>
<!-- Two languages on the heap, the rest off the heap -->
<cache alias="translations">
<resources>
<heap unit="entries">2</heap>
<offheap unit="MB">10</offheap>
</resources>
</cache>
saveLanguages(emf); // puts=3, entries=2
boolean englishCached = emf.getCache().contains(Language.class, enId); // false: evicted
emf.runInTransaction(em -> em.find(Language.class, enId));
// select l1_0.id,l1_0.code,l1_0.name from Language l1_0 where l1_0.id=?
// misses=1, puts=1
boolean frenchCached = emf.getCache().contains(Language.class, frId); // false: French made room for English
Loading English stored it again and evicted French. When the heap is smaller than the data that is read often, the cache keeps evicting and reloading entries, so most reads go to the database.
The translations region adds a second tier. An <offheap> tier stores entries outside the Java heap, so they do not add to garbage collection work, and the heap keeps only some of them. With the offheap tier, all three translations stay cached.
// translations: heap=2 entries, offheap=10 mb, entries=3
Translation hello = em.find(Translation.class, helloId); // greeting=Hello
Translation bonjour = em.find(Translation.class, bonjourId); // greeting=Bonjour
Translation hallo = em.find(Translation.class, halloId); // greeting=Hallo
// hits=3, misses=0, no SQL

Ehcache sets a few rules for the offheap tier, and they affect both speed and memory.
- Every value is serialized when it is stored and deserialized when it is read. Hibernate’s cache entries are Serializable, so no extra code is needed, but each read costs more than a heap read.
- A multi-tier cache always has a heap tier, and the heap must be smaller than the offheap tier.
- The offheap memory is not part of -Xmx, so we set -XX:MaxDirectMemorySize to fit the offheap size, as the Ehcache documentation recommends.
2.6. Regions Without a Cache Element
When a region is missing from ehcache.xml, Hibernate follows hibernate.javax.cache.missing_cache_strategy. The file ehcache-missing.xml declares only Language and the query regions, and leaves out translations and the collection, so we can see what each value of the setting does.
| Value | Startup | Cache that is created |
|---|---|---|
| create-warn (default) | Logs HHH90001006 for every missing region | No size limit, no expiry |
| create | No log | No size limit, no expiry |
| fail | Stops with a CacheException | None |
WARN org.hibernate.orm.cache - HHH90001006: Missing cache region [translations] was created with provider-specific default policies. Explicitly configure the region and its policies, or disable this warning by setting 'hibernate.javax.cache.missing_cache_strategy' to 'create'.
translations -> heap=9223372036854775807 entries
jakarta.persistence.PersistenceException: Unable to build Hibernate SessionFactory [persistence unit: ehcache-config]
Caused by: org.hibernate.cache.CacheException: On-the-fly creation of JCache Cache objects is not supported [com.howtodoinjava.hibernate.ehcacheconfig.Language.translations]
The heap of Long.MAX_VALUE entries means the cache grows until the JVM runs out of memory. We set missing_cache_strategy to fail in production, so a renamed entity or a typo in an alias stops the application at startup instead of creating an unbounded cache. With fail, Hibernate names the first missing region it builds, which is either the collection or the entity.
When we prefer a fallback, Ehcache’s JCache extension applies a template to every cache created at runtime.
<config xmlns="http://www.ehcache.org/v3"
xmlns:jsr107="http://www.ehcache.org/v3/jsr107">
<service>
<jsr107:defaults default-template="fallback"/>
</service>
<cache-template name="fallback">
<expiry>
<ttl unit="minutes">10</ttl>
</expiry>
<heap unit="entries">1000</heap>
</cache-template>
<cache alias="com.howtodoinjava.hibernate.ehcacheconfig.Language">
<heap unit="entries">500</heap>
</cache>
</config>
com.howtodoinjava.hibernate.ehcacheconfig.Language -> heap=500 entries, No Expiry
com.howtodoinjava.hibernate.ehcacheconfig.Language.translations -> heap=1000 entries, TTL of PT10M
default-query-results-region -> heap=1000 entries, TTL of PT10M
default-update-timestamps-region -> heap=1000 entries, TTL of PT10M
translations -> heap=1000 entries, TTL of PT10M
The missing regions get a limit from the template, but Hibernate still logs HHH90001006 for each of them. The timestamps region also got the 10 minute ttl, which is wrong for that region (see FAQ 3.5), so we still declare default-update-timestamps-region with <none/>.
2.7. Choosing a Concurrency Strategy per Region
The concurrency strategy in @Cache(usage = …) decides what Hibernate does with a region’s entry when a transaction inserts or changes the entity. We set it in the code, not in ehcache.xml, but it changes how many puts and misses each region sees. To compare the strategies, we cached one small Label entity with each strategy, inserted “Save”, changed it to “Save changes” and read it in a new session.
| Strategy | After persist() | Update | Next find() | Use it for |
|---|---|---|---|---|
| READ_ONLY | Cached (1 put) | Fails: Can’t update read-only object | Miss, SELECT | Data that never changes, with @Immutable |
| NONSTRICT_READ_WRITE | Not cached | Allowed; the entry is removed | Miss, SELECT | Rare changes, a short stale read is acceptable |
| READ_WRITE | Cached (1 put) | Allowed; the entry is replaced at commit | Hit, no SQL | Data that changes; the usual choice |
| TRANSACTIONAL | Cached (1 put) | Allowed; replaced at commit | Hit, no SQL | JTA with a transactional provider such as Infinispan |
With Ehcache, two of the strategies print a warning at startup.
WARN org.hibernate.orm.cache - HHH90001003: Read-only caching was requested for mutable entity [com.howtodoinjava.hibernate.ehcacheconfig.ReadOnlyLabel]
WARN org.hibernate.orm.cache - HHH90001008: Cache region [com.howtodoinjava.hibernate.ehcacheconfig.TransactionalLabel] has the access type 'transactional' which is not supported by [JCacheRegionFactory]. Ensure cache implementation supports JTA transactions.
Ehcache through JCache does not support TRANSACTIONAL. Hibernate accepts it with the warning, and in our test it behaved like READ_WRITE, without any JTA guarantee. For READ_ONLY, we add @Immutable to the entity, which removes the warning and makes Hibernate skip changes instead of failing. A list of supported currencies that changes only with a new release of the app is a good fit for READ_ONLY.
2.8. Reading Statistics per Region
With hibernate.generate_statistics on, the SessionFactory counts hits, misses and puts for each region separately. Global counters do not show a region with a bad configuration, so we check each one. We read English and its translations in two new sessions, and after that ran a cached JPQL query twice in the named region.
emf.runInTransaction(em -> em.find(Language.class, enId).getTranslations()); // [greeting=Hello]
// select t1_0.language_id,t1_0.id,t1_0.message_key,t1_0.text from Translation t1_0 where t1_0.language_id=?
emf.runInTransaction(em -> em.find(Language.class, enId).getTranslations()); // [greeting=Hello], no SQL
List<Language> languages = em.createQuery("select l from Language l order by l.code", Language.class)
.setHint(HibernateHints.HINT_CACHEABLE, true)
.setHint(HibernateHints.HINT_CACHE_REGION, "language-queries")
.getResultList(); // [de (German), en (English), fr (French)], twice, one SELECT
The Statistics interface has one method per region type, and each method returns a CacheRegionStatistics.
Statistics stats = emf.unwrap(SessionFactory.class).getStatistics();
String[] regions = stats.getSecondLevelCacheRegionNames(); // all regions in use
CacheRegionStatistics languageStats = stats.getDomainDataRegionStatistics(Language.class.getName()); // entity and collection regions
CacheRegionStatistics queryStats = stats.getQueryRegionStatistics("language-queries"); // query result regions
CacheRegionStatistics anyStats = stats.getCacheRegionStatistics("language-queries"); // either kind
for (String region : stats.getSecondLevelCacheRegionNames()) {
CacheRegionStatistics s = stats.getCacheRegionStatistics(region);
System.out.println(region + ": hits=" + s.getHitCount() + " misses=" + s.getMissCount() + " puts=" + s.getPutCount());
}
default-query-results-region: hits=0 misses=0 puts=0
com.howtodoinjava.hibernate.ehcacheconfig.Language: hits=2 misses=0 puts=0
translations: hits=1 misses=0 puts=0
language-queries: hits=1 misses=1 puts=1
com.howtodoinjava.hibernate.ehcacheconfig.Language.translations: hits=1 misses=1 puts=1
The collection region missed once and was filled by the first session. The second session found the collection and after that the “Hello” entry in translations. With Ehcache 3, four of these calls give results that need a closer look.
| Call | Result with Ehcache 3 |
|---|---|
| getDomainDataRegionStatistics(“default-query-results-region”) | IllegalArgumentException: Region name [default-query-results-region] referred to a query result region, not a domain data region |
| getElementCountInMemory() | -9223372036854775808 (Long.MIN_VALUE): JCache does not report it |
| getSecondLevelCacheRegionNames() | Has no default-update-timestamps-region; language-queries appears after its first use |
| Counting the entries of a cache | Iterate cacheManager.getCache(alias): 3 entries for three languages |
3. Hibernate Ehcache Configuration FAQs
3.1. Where Does Hibernate Look for ehcache.xml?
Hibernate reads the file from the location that hibernate.javax.cache.uri names. A plain name such as ehcache.xml or ehcache-expiry.xml is read from the classpath, so the file goes into src/main/resources. For a file outside the application, we pass a file URI.
.property("hibernate.javax.cache.uri", "ehcache.xml") // classpath
.property("hibernate.javax.cache.uri", "file:/path/to/ehcache.xml") // file system
Without the hibernate.javax.cache.uri property, Hibernate uses the default JCache CacheManager, where no region is declared. So Hibernate logs HHH90001006 for every region and creates each cache without a size limit.
3.2. Why Does key-type Break the Hibernate Cache?
The <key-type> check breaks because Hibernate does not store our ids and entities. Some tutorials declare <key-type>java.lang.Long</key-type> and the entity class as <value-type>, but Hibernate stores its own key objects and dehydrated entries (arrays of column values). So the type check fails when the first entry is stored.
jakarta.persistence.RollbackException: Error while committing the transaction [Unable to perform afterTransactionCompletion callback: Invalid key type, expected : java.lang.Long but was : org.hibernate.cache.internal.BasicCacheKeyImplementation]
Caused by: java.lang.ClassCastException: Invalid key type, expected : java.lang.Long but was : org.hibernate.cache.internal.BasicCacheKeyImplementation
The error comes after the database commit. The three languages were saved, but the caller gets a RollbackException, so a user who saves a form sees an error page although the data is in the database. We leave key-type and value-type out, and both default to java.lang.Object.
3.3. Does hibernate.cache.region_prefix Change the Cache Names?
Yes. With hibernate.cache.region_prefix=i18n, Hibernate asks Ehcache for i18n.com.howtodoinjava.hibernate.ehcacheconfig.Language, i18n.translations, i18n.default-update-timestamps-region and so on. The aliases in ehcache.xml must carry the prefix too, because in our test the unprefixed caches stayed unused and the prefixed ones were created without a limit. The statistics keep the names without the prefix.
// caches: i18n.com.howtodoinjava.hibernate.ehcacheconfig.Language, i18n.translations, ...
// statistics: com.howtodoinjava.hibernate.ehcacheconfig.Language, translations, ...
CacheRegionStatistics translationStats = stats.getDomainDataRegionStatistics("translations"); // works without the prefix
A prefix is useful when two EntityManagerFactory instances share one CacheManager and have entities with the same names.
3.4. How Do Ehcache 2 Settings Map to Ehcache 3?
Hibernate 5 apps configured Ehcache 2, but Hibernate 6 removed the hibernate-ehcache module, and Ehcache 3 has a new XML format. The CacheProvider of Hibernate 3 is older still. Each old setting has an Ehcache 3 equivalent.
| Ehcache 2 with Hibernate 5 | Ehcache 3 with Hibernate 7 |
|---|---|
| <cache name=”…” maxElementsInMemory=”10000″> | <cache alias=”…”><heap unit=”entries”>10000</heap></cache> |
| timeToLiveSeconds=”600″ | <expiry><ttl unit=”seconds”>600</ttl></expiry> |
| timeToIdleSeconds=”300″ | <expiry><tti unit=”seconds”>300</tti></expiry> |
| eternal=”true” | <expiry><none/></expiry> |
| overflowToDisk=”true” | A <disk> tier inside <resources> |
| <defaultCache> | <jsr107:defaults default-template=”…”/> |
| net.sf.ehcache.configurationResourceName | hibernate.javax.cache.uri |
| EhCacheRegionFactory | hibernate.cache.region.factory_class=jcache |
| <cache usage=”read-write”/> in hbm.xml | @Cache(usage = CacheConcurrencyStrategy.READ_WRITE) |
Ehcache 3 has no ttl and tti on the same cache, but a custom ExpiryPolicy class (<expiry><class>…</class></expiry>) can combine them. Old Hibernate 5 code that reads the entity with load() needs a change too, because Session in Hibernate 7 no longer has load(Class, id), so we use em.find().
3.5. Should the Timestamps Region Expire?
No. Hibernate keeps the time of the last insert, update or delete of each table in default-update-timestamps-region and compares it with the time a query result was cached. The timestamps region should have no expiry and no eviction, and if it has a timeout at all, the timeout must be longer than that of every query results region, as the Hibernate user guide recommends. If a timestamp disappears first, Hibernate can no longer tell that a table changed after a result was cached. We give the region <none/> and a heap larger than the number of cached tables.
4. Conclusion
An ehcache.xml for Hibernate has one <cache> per region, and the alias must equal the region name. Entity regions use the class name and collection regions add the field name to it, whereas the query cache uses two default regions. Templates keep the shared limits in one place, and ttl limits how stale an entry can be. We size the heap in entries and add an offheap tier for entries that do not fit.
Setting missing_cache_strategy to fail reports forgotten regions at startup, and the per-region statistics show that each cache is used.
5. References
- Hibernate ORM 7.4 User Guide: JCache
- Hibernate ORM 7.4 User Guide: JCache missing cache strategy
- Hibernate ORM 7.4 User Guide: Query cache regions
- Hibernate ORM 7.4 User Guide: Caching configuration properties
- CacheConcurrencyStrategy JavaDoc (Hibernate 7.4)
- CacheRegionStatistics JavaDoc (Hibernate 7.4)
- Cacheable JavaDoc (Jakarta Persistence 3.2)
- Ehcache 3 XML Configuration
- Ehcache 3 Expiry
- Ehcache 3 Storage Tiers
- Ehcache 3 JCache Provider and Default Templates
- HHH-14544: Ehcache default caches and performance
Happy Learning !!
can we apply caching for relations like onetone, onetomany … in JPA,if applied what is the behaviour of caching will it work for all relations for read-write strategy?
Thank you for detailed tutorial.
i would like to post one update regarding configuration in hiernate.cfg.xml.
From version 4.x you should use following for hibernate.cache.region.factory_class property.
Thanks for the update. Much appreciated.
Hi,
I have 10 level of inheritance and some of them has-a relation with some entities (with one to one and one to many)
which are lazy loading by default but i want to load eagerly, but for lazy loading query caching is working but for eager loading its hitting db always.kindly reply
I did a quick google search and looks like it is common problem. I am working on it right now.
Hi Lokesh
I have executed the code given by you. I have few questions and issues
System.out.println(HibernateUtil.getSessionfactory().getStatistics().getEntityFetchCount());
System.out.println(HibernateUtil.getSessionfactory().getStatistics().getSecondLevelCacheHitCount());
The above prints 0 and 2 but not 1 and 1
Following are my questions
1. How do i know whether the ehcache is being read and where exactly it is stored(I mean the physical location)
diskStore path=”java.io.tmpdir”. After my program completes i dont see any object at this location.
What i am expecting is what actually is created when we configure ehcache.xml. (Is it any object or any file. What is it )
This is my ehcahe.xml
This is placed in src/maim/resources folder along with hibernate.cfg.xml
Try replacing “java.io.tmpdir” with “c:tempcache” or any such actual physical location. There you should be able to see actual cache files. A similar use case is discussed here: https://howtodoinjava.com/
Output on first read will be different from repeated reads because entities are read from secondary cache every time after the first read.
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="ehcache.xsd"> <diskStore path="c:\temp\cache"/> <cache name="com.pramati.dto.Department" maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="300" timeToLiveSeconds="600" overflowToDisk="true" /> </ehcache>The folder specified in diskStore path=”c:\temp\cache” is getting created but i don’t see any objects in that folder.
Once my program execution is completed how do i clear the cache and make sure that next time i run this program the statistics show 1 and 1 instead of 0 and 2
Can you please give me your hibernate.cfg.xml code
And consider using diskPersistent=”true” as well.
<?xml version='1.0' encoding='utf-8'?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <property name="connection.driver_class">org.postgresql.Driver</property> <property name="connection.url">jdbc:postgresql://localhost:5432/hibernatexml</property> <property name="connection.username">postgres</property> <property name="connection.password">postgres</property> <!-- JDBC connection pool (use the built-in) --> <property name="connection.pool_size">1</property> <!-- SQL dialect --> <property name="dialect">org.hibernate.dialect.PostgreSQLDialect</property> <!-- Enable Hibernate's automatic session context management --> <property name="current_session_context_class">thread</property> <property name="cache.use_second_level_cache">true</property> <property name="cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property> <property name="net.sf.ehcache.configurationResourceName">/ehcache.xml</property> <!-- Echo all executed SQL to stdout --> <property name="show_sql">true</property> <property name="hbm2ddl.auto">create</property> <mapping resource="com/pramati/dto/Department.hbm.xml"></mapping> </session-factory> </hibernate-configuration>Here is my ehcache.xml
Place code as above instructions in red.
Hi Lokesh,
It is very well explained but I still have a query regarding point 7 in How second level cache works? My Question is how application knows that the db is updated directly and it has to invalidate the session.
As I mentioned in point 7 that “there is no way that second level cache update itself“. So application can never know when DB got updated automatically. Either you need to tell application to invalidate it’s whole cache, OR application will do itself when ‘timeToLiveSeconds’ is passed. After this configured time, cache is re-build again whether DB was updated or not. It’s routine process.
So you mean, at least for some time application will not get updated data. Isn’t it a problem which we are overlooking at? Kindly let me know if you find any way we can address this?
Yes, absolutely true. For some time, application will be working with stale data/cache. Unfortunately, there is no way (If any exist then I am not aware of) to notify application/hibernate. Hibernate refresh data only when it is updated through it’s own way. If you use backdoor [direct update in DB] then how hibernate will come to know about this.
Usually to handle such cases, in production applications I have worked on, there was an admin panel where you click “Flush All Cache” button and it will re-generate the whole cache again.
This API would remove all entities for a specific cache region as said here there should be some button to flush out cache entities.
Also, the application which I work on a think wrapper written around DB which sends a notification(model configuration needed for mentioning the table) if any changes happen on the table. Logic is build which receives the event and updates the cache
ehcache or other variants are always work on 2. level cache or is it possible to configure them work on 1. level?Thanks in advance
At first level, there is always hibernate default cache and you cannot even disable it. You can choose anything at only second level cache.
when we set or change entity and commit the transection session cache is updated.So 2. level cache is also updated or not? if so the other sessions will still use old data because they are referring first level cache before second level maybe second level cache changes trigger to all sessions update their data from 2. level context.
Please try to clear your doubt from this article. Everything is explained here. If still have some doubt, please let me know.
Maybe It can help: https://www.ibm.com/support/pages/node/166541
Hello I am just implementing Query level cache in my code using annotation and currently facing a problem ….can you please clarify that do we need to use @Cache() at the entity level if we are implementing Query Level cache only.
No. You don’t need to use @Cache at entity level for implementing Query level cache only.
In attached zip file there is ehCache.xml file is not present?
Look at path: hibernate-test-project / .settings / ehcache.xml
Thanks Lokesh!
I have create a stand alone program for ehcache. I have deleted/renamed ehcache.xml even after that i can see the cached file in java.io.tmpdir” path.
Could you please let me know how/where to use ehcache.xml. Can i rename it/can path updated from java.io.tmpdir to any-other path.
Appreciate your response.
I am applying EHCache for Hibernate4.
This is my configuration. In entity, I configured like this
I have add the jars : ehcache-core-2.4.5.jar,hibernate-ehcache-4.1.0.Final.jar
Problem: For the first click it getting the objects putting into 2nd level cache(ex: SecondLevelCachePutCount -30), from the next click onwards it is getting the 7 objects which are there in the second level cache. Now it is updating SecondLeveCacheMissCount, SecondLevelHitCount (if we observe with JConsole).It should not go to DB again.
Please Help me……
ehcache and OSCache useful in real time projects or not?
Both are powerful to be used in real time projects.
Hi Lokesh,
Can you explain me about stale object in Hibernate?
A stale persistent object is no longer valid. Its fields have default values or values left over from previous transactions and should not be used. An object can also become stale when the transaction in which it was accessed is aborted because of a deadlock or another action that caused AbortException to be signaled.
I will cover more in any future post.
Sorry about my bad English.
Firstly I want to thank you for all these tutorials, secondly, I followed all steps mentioned above but my output was like that :
Output:
would you explain to me why?
Probably you have run the example more than one time. Second level cache store the information in file system and next time when you run the example, it refers from there only. So, first statement will print 0 because it never gone to database. For second statement, it should print 1. Try putting “hibernate.generate_statistics” to true in hibernate.cfg.xml.
Also try inspect values from other methods in : https://docs.jboss.org/hibernate/orm/3.2/api/org/hibernate/stat/Statistics.html
yes, After adding “hibernate.generate_statistics” I’ve got the desired result. many thanks.
where should i keep ehcache.xml file
Parallel to “hibernate.cfg.xml”