[Hibernate] – How to work with cache ?

What is cache ?

To improve performance, we could then put the results of searches in some intermediate place in the application itself to be consulted before going to the bank. That is, for a given first search will consult the intermediate space. If you found one for this search (hit), we will use. Otherwise, effectively will hold the query to the database storing the result in space. Note that we will only really remote communication to the database if they are not found stored results in the intermediate space (miss). This space is what is commonly called the cache.

Hibernate Cache Structure 

hibernate_cache

First Level

The known as first-level cache prevents the entity carry twice the database. This means that when we execute the find method twice for the same id, only the first call will trigger a query. When we run find for the second time in search of the same object using the same EntityManager he will know that that entity has been charged and will reuse the results without performing a new search the database.

Configuration:

props.setProperty("hibernate.hbm2ddl.auto", "update");

Example:

public class TestCache {
    public static void main(String[] args) {

        ApplicationContext ctx = 
            new AnnotationConfigApplicationContext(JpaConfigurator.class);

        EntityManagerFactory emf = 
           (EntityManagerFactory) ctx.getBean(EntityManagerFactory.class);
        EntityManager em = emf.createEntityManager();

        Product product = em.find(Product.class, 1);
        System.out.println("Name: " + product.getName());

        Product otherProduct = em.find(Product.class, 1);
        System.out.println("Name: " + otherProduct.getName());
    }
}

So, in this situation, in the same EntityManager don’t will search again this data in the database. If you do this test you will se that has only one select relative the first search.

Challenge :

Look the code below. What is happen in this case ? Will we have only one select or not ?

public class TestCache {
    public static void main(String[] args) {

        ApplicationContext ctx = new AnnotationConfigApplicationContext(JpaConfigurator.class);

        EntityManagerFactory emf = (EntityManagerFactory) ctx.getBean(EntityManagerFactory.class);

        EntityManager em = emf.createEntityManager();
        EntityManager em2 = emf.createEntityManager(); 

        Product product = em.find(Product.class, 1);
        System.out.println("Name: " + product.getName());

        Product otherProduct = em2.find(Product.class, 1); 
        System.out.println("Name: " + otherProduct.getName());

    }
}

In this situation, we will find 2 selects. Why ? Because ins’t the same EntityManager. Remember, the first level is valid only for each EntityManager.

Second Level

Cache space that is shared among the various Entity Managers of our application and is used when the first-level cache does not have the desired information.

In general, dealing with second-level cache is much more complex than the first level as the possibility of dealing with outdated data (stale) is much higher. The objects of this cache are invalidated when there is a write operation on the entity (such as update). If there is any other system updating data in the database without going through JPA its use may become unviable.

Configuration:

<property name="hibernate.cache.use_second_level_cache" value="true" />

 EhCache Provider

JBoss Wildfly already has an embedded provider called infinispan.

How to configure it ?

To configure it as provider, add the hibernate.cache.region.factory_class property:

public class JpaConfigurator {
    ...
    @Bean
    public LocalContainerEntityManagerFactoryBean getEntityManagerFactory(DataSource dataSource) {

        ...
        props.setProperty("hibernate.cache.use_second_level_cache", "true");
        props.setProperty("hibernate.cache.region.factory_class", 
            "org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory");
        ...
    }
}

Or in persistence.xml:

<property name="hibernate.cache.region.factory_class" 
value="org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory" />

How to use it in the classes ? How to define ?

@Entity
@Cache(usage=CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class Product {

However, have a lot of cache strategies, we can define to invalidate the cache after update for example, other else, then we can define always is necessary go to the database after an update in this Entity.

READ_ONLY : should be used when an entity should not be modified.

READ_WRITE : should be used when an entity can be modified and there are great chances that changes in their condition occur simultaneously, is the strategy that consumes more resources.

NON_STRICT_READ_WRITE: strategy should be used when an entity can be modified but it is unusual that the changes occur at the same time. It consumes less resources that READ_WRITE strategy and is ideal when there is no problem to read inconsistent data when there is simultaneous changes.

TRANSACTIONAL : should be used in JTA environments, such as in application servers.

More information :

https://docs.jboss.org/hibernate/orm/3.2/api/org/hibernate/cache/CacheConcurrencyStrategy.html

Queries Cache

We can cache the result of a query made to certain parameters, we can always look in the cache instead of going to the database. For this we use the query cache that is off by default. To turn it on, we need to add one more property in the configuration of JPA.

Configuration

public class JpaConfigurator {
    ...
    @Bean
    public LocalContainerEntityManagerFactoryBean getEntityManagerFactory(DataSource dataSource) {

        ...
        props.setProperty("hibernate.cache.use_second_level_cache", "true");
        props.setProperty("hibernate.cache.use_query_cache", "true");
        props.setProperty("hibernate.cache.region.factory_class", "org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory");
        ...
    }
}

Once activated, we need to tell Hibernate we want it to save in the cache the results of any query. And we do this through a tip (hint) in the query itself through setHint method of the Query interface:

query.setHint("org.hibernate.cacheable", "true");

Example:

public List<Product> getProducts(String nome, Integer categoriaId, Integer lojaId) {
    ...

    TypedQuery<Product> typedQuery = em.createQuery(query.where(test));
    typedQuery.setHint("org.hibernate.cacheable", "true");

    return typedQuery.getResultList();
}

 

 

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s