Hibernate uses two different caches for objects: first-level cache and second-level cache.
First-level cache is associated with the Session object, while second-level cache is associated with the SessionFactory object. By default, Hibernate uses first-level cache on a per-transaction basis. Hibernate uses this cache mainly to reduce the number of SQL queries it needs to generate within a given transaction.
Hibernate always tries to first retrieve objects from the session and if this fails it tries to retrieve them from the second-level cache. If this fails again, the objects are directly loaded from the database. Hibernate’s static initialize() method, which populates a proxy object, will attempt to hit the second-level cache before going to the database. The Hibernate class provides static methods for manipulation of proxies.
The query cache is responsible for caching the results and to be more precise the keys of the objects returned by queries. Let us have a look how Hibernate uses the query cache to retrieve objects. In order to make use of the query cache we have to modify the order loading example as follows.
To use the query cache, you use the setCacheable(Boolean) method of the Query class.
Session session = SessionFactory.openSession();
Query query = session.createQuery("from Order"); query.setCacheable(true);
List users = query.list();
Setting Up a SessionFactory Cache
The following properties are available in the Hibernate Configuration files to handle cache setup:
hibernate.cache.provider_class: Indicates the class name of a custom CacheProvider.
hibernate.cache.use_minimal_puts: Optimizes second-level cache operation to minimize writes, at the cost of more frequent reads (useful for clustered caches). Possible values are true and false.
hibernate.cache.use_query_cache: Enables the query cache. The query cache is disabled by default. Possible values are true and false.
hibernate.cache.region_prefix: Provides a prefix to use for second-level cache region names.
The two important properties are hibernate.cache.provider_class and hibernate.cache.use_query_cache.
The following table summarizes the caches and what they provide.
|Cache||Provider Class||Type||Cluster Safe||Query Cache Supported|
|TreeCache||net.sf.hibernate.cache.TreeCacheProvider||Clustered(IP multicast), transactional||Yes (ClusterReplication)||No|
For more info on EHCache please go through EHCache example in Hibernate
For more info on OSCache please go through OSCache example in Hibernate
Different caching strategies
Read-only: This strategy is useful for data that is read frequently but never updated. This is by far the simplest and best-performing cache strategy.
Read/write: Read/write caches may be appropriate if your data needs to be updated. They carry more overhead than read-only caches. In non-JTA environments, each transaction should be completed when Session.close() or Session.disconnect() is called.
Nonstrict read/write: This strategy does not guarantee that two transactions won’t simultaneously modify the same data. Therefore, it may be most appropriate for data that is read often but only occasionally modified.
Transactional: This is a fully transactional cache that may be used only in a JTA environment.
When Not to Use Caching (authors – Eric Pugh, Joseph D. Gradecki)
As we know, caching takes up resources in your application and requires careful tuning to balance the amount of memory used versus the benefit gained. There are definitely times when caching isn’t indicated:
- If your database is being modified by multiple applications, then Hibernate won’t be able to ensure that the data is valid. You may be able to deal with this issue by specifying a version or timestamp property for your object and using the Session.lock() method to verify that the object hasn’t been changed.
- Some data is retrieved and then not reused so that the data expires from the cache. In this case, there is no point in caching the information and taking up more memory. Caching only helps when the same data is used multiple times within the expiry time period.
- The database provides additional functionality, such as auditing your data retrieval. Some applications have a requirement that all SQL statements are logged in order to track who is using what data. Typically this is done at the database level via triggers. In these situations, the cache may be handing the data to various users but without reissuing the SQL statements. This
would bypass the SELECT statements that are required for the database triggers to fire.
- The application is preserving the first-level session cache for long periods of time. Often in a thick client application a single session is created when the application is started. This session is held open for the lifetime of the application. In this case, the session provides all the caching required.
- You’re loading very large numbers of objects. If you’re loading and parsing millions of objects, then there may not be enough memory available to cache them. However, remember that you don’t have to cache everything. Many applications have large subsets of data that are reused frequently.
Thanks for your reading. Please do not forget to leave a comment.