Search code examples
javahibernatejpacachingehcache

Why do we have to use @Cacheable as well as @Cache in Hibernate for second-level of cache?


I want to know why we have to use 2 annotations to use the second-level of cache in Hibernate.

We declare :

@Cacheable
@Cache

Why don't we declare directly @Cache with the options?


Solution

  • I want to know why we have to use 2 annotations to use the second-level of cache in Hibernate.

    You don't need to use both, but you can. To enable the second level cache on an entity, you have 2 options:
    (1) Use @Cacheable (more below)
    (2) Use @Cache(more below)

    @Cacheable is the JPA cache interface(used on an entity) that together with shared cache mode property decides whether an entity will be cached or not.

    By default, entities are not part of the second level cache and we recommend you to stick to this setting. However, you can override this by setting the shared-cache-mode element in your persistence.xml file or by using the jakarta.persistence.sharedCache.mode property in your configuration file. The following values are possible:

    ENABLE_SELECTIVE (Default and recommended value) Entities are not cached unless explicitly marked as cacheable (with the @Cacheable annotation).

    DISABLE_SELECTIVE Entities are cached unless explicitly marked as non-cacheable.

    ALL Entities are always cached even if marked as non-cacheable.

    NONE No entity is cached even if marked as cacheable. This option can make sense to disable second-level cache altogether.

    As per this: Some developers consider that it is a good convention to add the standard @javax.persistence.Cacheable annotation as well (although not required by Hibernate).

    @Cache is the Hibernate cache interface, recommended to specify the CacheConcurrencyStrategy on a per entity basis instead of the global setting hibernate.cache.default_cache_concurrency_strategy.

    Why don't we declare directly @Cache with the options ?

    That's exactly what is recommended, so that besides telling Hibernate that you want to enable second level caching for this entity, you also keep the concurrency strategy on a per entity basis, instead of global, as explained above. Remember to enable the second level cache(as by default, it' not), by using hibernate.cache.use_second_level_cache. A basic configuration of second level cache(I am using Spring Boot):

    //In build.gradle:
    implementation 'org.hibernate:hibernate-ehcache' //IMPORTANT: If you are specifying a version, make sure that it is the same version as your Hibernate version that you are using.
    
    //Hibernate properties(can also be externalized to application.properties):
    properties.put("hibernate.cache.use_second_level_cache", "true"); //hibernate.cache.use_second_level will also work
    properties.put("hibernate.cache.region.factory_class", "org.hibernate.cache.ehcache.EhCacheRegionFactory"); //act as a bridge between Hibernate and the caching provider
    
    emf.setJpaProperties(hibernateProperties());
    
    //In the entity class:
    import org.hibernate.annotations.Cache;
    import org.hibernate.annotations.CacheConcurrencyStrategy;
    @Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
    public class MyClass implements Serializable {
    

    Note 1: The above is using Ehcache 2.x
    Note 2: To use Ehcache 3.x, use the following properties:

    properties.put("hibernate.javax.cache.provider", "org.ehcache.jsr107.EhcacheCachingProvider");
    properties.put("hibernate.cache.region.factory_class", "jcache");
    

    Note 3: Collections are not cached by default, you need to explicitly mark them with @Cache.

    BONUS:
    If you want to check the statistics:

    //Add this property:
    properties.put("hibernate.generate_statistics", "true");
    
    //And somewhere in your code:
    sessionFactory.getStatistics();