Hibernate / Hibernate Caching
Caching optimizes the application performance and it lies between the application and the database to minimize the number of database calls as many as possible to provide better performance for performance critical applications.
Hibernate provides three type of cache.
- First level cache : First level cache is associated with session object in hibernate and hibernate uses it by default.
- Second level cache : Second level cache associates with session factory object. To avail second level cache, we need to configure it in our application.
- Query level cache : use query-level cache if you need to cache actual query results, rather than just persistent objects. The query cache should always be used in conjunction with the second-level cache.
Second level cache is on SessionFactory level. All the second level cache provider class must implement org.hibernate.cache.spi.CacheProvider by configuring the property hibernate.cache.provider_class. Hibernate provides open source cache providers also.
Hibernate supports four open-source cache implementations.
- EHCache (Easy Hibernate Cache),
- OSCache (Open Symphony Cache),
- Swarm Cache,
- and JBoss Tree Cache.
Each cache has different performance, memory use, and configuration possibilities.
The below are the advantages of EHCache. (org.hibernate.cache.EhCacheProvider)
- It is fast.
- lightweight.
- Easy-to-use.
- Supports read-only and read/write caching.
- Supports memory-based and disk-based caching.
- Does not support clustering.
The below are the advantages of OSCache. (org.hibernate.cache.OSCacheProvider)
- It is powerful .
- flexible package.
- supports read-only and read/write caching.
- Supports memory- based and disk-based caching.
- Provides basic support for clustering via either JavaGroups or JMS.
- SwarmCache (org.hibernate.cache.SwarmCacheProvider) is a cluster-based caching.
- supports read-only or nonstrict read/write caching.
- appropriate for applications those have more read operations than write operations.
JBoss TreeCache (org.hibernate.cache.TreeCacheProvider) is a powerful replicated and transactional cache which is useful when we need a true transaction-capable caching architecture .
To configure shared cache mode, hibernate provides javax.persistence.sharedCache.mode and it allows four values.
- ENABLE_SELECTIVE: Entities will not be cached until entity will be annotated by cacheable.
- DISABLE_SELECTIVE : Those entities are cached which are explicitly not annotated with cacheable.
- ALL : Every entities will be cached.
- NONE: No entity will be cached.
There are 4 different CacheMode.
- CacheMode.NORMAL : This mode allows to read and write data in second level cache.
- CacheMode.GET : In this mode, data will be read but will not be written in second level cache.
- CacheMode.PUT : Data will be written but not will be read from second level cache.
- CacheMode.REFRESH : Data will be written and will not be read from second level cache. The difference between Put and Refresh is that CacheMode.REFRESH will bypass
Hibernate can cache query results. When a query is fetched frequently then caching query result is useful. Hibernate has an overhead to enable query cache because to keep updated the query result, hibernate has to track the changes in database.
We configure it as below. hibernate.cache.use_query_cache = "true"
In hibernate, cache concurrency strategy can be set globally using the property hibernate.cache. default_cache_concurrency_strategy. The allowed values are,
- read-only : supported by ConcurrentHashMap, EHCache, Infinispan.
- read-write : supported by ConcurrentHashMap, EHCache.
- nonstrict-read-write : supported by ConcurrentHashMap, EHCache.
- Transactional : supported by EHCache, Infinispan.
In hibernate first level cache is provided by default. Hibernate do that by session. But second level cache can be enabled explicitly. Second level Caching in Hibernate can be done at three levels.
Enable Hibernate Second Level Cache Globally.
We need to set a property in our persistence configuration file. The property is hibernate.cache. default_cache_concurrency_strategy. By setting this property, all tables can be cached at second level.
Enable Hibernate Second Level Cache at Table Level by Annotation.
In Hibernate Annotation, we use @Cache to set table level cache. We also need to set @Cacheable. The class CacheConcurrencyStrategy of the package org.hibernate.annotations, provides the caching.
Enable Hibernate Second Level Cache at Column Level by Annotation.
In Hibernate Collections to cache the collection at column level @Cache is used and CacheConcurrencyStrategy class provides the caching. We need to set it at property level in our entity.
This entry forces Hibernate to store data in the second-level cache (L2 cache) in a more human-friendly format.
Whenever hibernate session try to load an entity, it first looks for cached copy of entity in first level cache (associated with particular hibernate session).If cached copy of entity is present in first level cache, it is returned as result of load method.
If there is no cached entity in first level cache, then second level cache is looked up for cached entity.If second level cache has cached entity, it is returned as result of load method. But, before returning the entity, it is stored in first level cache also so that next invocation to load method for entity will return the entity from first level cache itself.
If entity is not found in first and second level cache, then database query is executed and entity is stored in both cache levels, before returning as response of load() method.
Second level cache validate itself for modified entities, if modification has been done through hibernate session APIs. If some user or process make changes directly in database, then there is no way that second level cache update itself until "timeToLiveSeconds" duration has passed for that cache region. In this case, it is good idea to invalidate whole cache and let hibernate build its cache once again.
Hibernate first level cache is enabled by default and there is no way to disable it. However hibernate provides methods using which selected objects can be deleted from the cache or clear the cache completely.
session.evict(obj) will remove the object instance from the session cache. Therefore if you are saving the object for the first time, you will have to explicitly commit via session.save(obj) before evicting the object from the cache. Subsequent update calls should follow through session.saveOrUpdate(obj) or session.update(obj) before calling calling evict to remove the loaded object from the cache.
evict() evicts a single object from the session. clear() evicts all the objects in the session. Calling clear() is like calling evict() on every object associated with the session.
We can use session contains() method to check if an object is present in the hibernate cache or not, if the object is found in cache, it returns true or else it returns false.
eternal attribute maintains the data freshness and if true it indicates that mappings never expire.
maxElementsInMemory is a count based capacity control that limits the cache size so that you do not exhaust memory of your application by filling up a cache.
Hibernate second level cache can be disabled by,
- setting use_second_level_cache as false,
- using CACHEMODE.IGNORE,
- Using cache provider as org.hibernate.cache.NoCacheProvider.
timeToIdleSeconds enables cached object to be invalidated if it has not been requested for specified ('timeToIdleSeconds' ) seconds.
timeToLiveSeconds enables cached object to be invalidated after that many seconds regardless of how many times or when cache was requested last time.
Note that the eternal attribute, when set to 'true', overrides timeToLive and timeToIdle so that no expiration can take place.
- read-only,
- nonstrict read-write,
- read-write,
- and transactional.
Second Level Cache caches entities. Also, the second level cache also allows users to cache entity relationship information. Hibernate provides a collection cache, where it caches the primary keys of entities that are members of a collection field in another entity type. Say, for example, we have two entity types, Book and Auther, where an Auther participates in a many-to-one relationship with a Book.
import javax.persistence.*; import org.hibernate.annotations.Cache; import org.hibernate.annotations.CacheConcurrencyStrategy; @Entity @Cache(usage=CacheConcurrencyStrategy.TRANSACTIONAL) public class Book{ @Id private Integer id; private String name; @Cache (usage=CacheConcurrencyStrategy.TRANSACTIONAL) @OneToMany(mappedBy="auther", fetch=FetchType.EAGER, cascade=CascadeType.ALL) private Set authers; ..... }
When using native SQL queries you have to address the risk of invalidating existing cache when query cache enabled.
Using SQLQuery, Hibernate couldn't know what cache regions you might affect, however you can explicitly provide the instruction. With the explicit instruction, hibernate know which query caches to invalidate, otherwise it may discard all the cache entries.
This only affects the second level cache.