In this tutorial I will tell you different types of caches in Hibernate framework. Hibernate is an ORM (Object Relational Mapping) framework. Hibernate uses two different caches for objects: first-level cache and second-level cache. If you have queries that run over and over, with the same parameters, query caching provides performance gains.
Caching introduces overhead in the area of transactional processing. For example, if you cache results of a query against an object, Hibernate needs to keep track of whether any changes have been committed against the object, and invalidate the cache accordingly. In addition, the benefit from caching query results is limited, and highly dependent on the usage patterns of your application. For these reasons, Hibernate disables the query cache by default.
First 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.
Second Level Cache
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’s see 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();
Setup 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 Cache Provider.
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
The following table summarizes the caches and what they provide.
|Query Cache Supported
|Clustered (IP Multicast)
|Yes (Cluster Invalidation)
|Clustered (IP Multicast), Transactional
|Yes (Cluster Replication)
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.disconnect() is called.
Non-restrict 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 it is known, 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.
That’s all about different caches in Hibernate framework.