Ads Top

Hibernate First Level Cache

Introduction

Caching significantly improves the performance of an Application by holding frequently accessed data which is expensive to get. Caching lies between the Application layer and the database layer, and reduce the number of queries to the database to improve the performance of the Application. We can also store a disk file or a report in the cache for faster retrieval. In this post, we will look into Hibernate First level cache.

Table of Contents

  1. Hibernate First Level Cache
  2. First Level Cache Example
  3. Removing Objects from First Level Cache

Hibernate First Level Cache

First level cache in hibernate belongs to the session and it is enabled by default. We do not need to do anything to get this working as Hibernate uses it by default and it can not be disabled.

When we load an entity from the database, the copy of the entity object is saved in the session cache. If we try to fetch the same entity again in the same session, it will be returned from the cache thus avoiding a database call and no SQL query will be executed. If we update the same object multiple times in the same transaction, then it is only updated in the session cache. On committing the transaction the final state of the object is persisted to the database, thus avoiding multiple calls to the database.

The scope of the cached object is of the session, ones the session is closed, cache objects are no longer available. Also, the objects in the session cache can not be shared between sessions which means other session objects can not access it. To make cached objects available between sessions we need to use Hibernate Second Level Cache.

First Level Cache Example 

Consider the below code, we have fetched the city details by ID twice, since the first level is enabled by default, we can observe that there is only one SQL query is being executed.

CityService.java

  package com.devtalkers.jpacache.service;   
  import com.devtalkers.jpacache.entity.City;   
  import com.devtalkers.jpacache.repository.CityRepository;   
  import org.springframework.beans.factory.annotation.Autowired;   
  import org.springframework.stereotype.Service;   
  import javax.transaction.Transactional;   
    
  @Service   
  public class CityService {   
     
   @Autowired   
   private CityRepository cityRepository;   
     
   @Transactional   
   public City getCityById(Integer id){   
    City city = cityRepository.getOne(id);//get city by id   
    city = cityRepository.getOne(id);//get city by id again   
    return city;   
   }   
  }   

Below is the Rest Controller, here we have called the getCityById method of the CityService
shown above.

CityController.java

  package com.devtalkers.jpacache.rest;   
  import com.devtalkers.jpacache.entity.City;   
  import com.devtalkers.jpacache.service.CityService;   
  import org.springframework.beans.factory.annotation.Autowired;   
  import org.springframework.http.HttpStatus;   
  import org.springframework.http.ResponseEntity;   
  import org.springframework.web.bind.annotation.GetMapping;   
  import org.springframework.web.bind.annotation.PathVariable;   
  import org.springframework.web.bind.annotation.RestController;   
    
  @RestController   
  public class CityController {   
     
   @Autowired   
   private CityService cityService;   
     
   @GetMapping("/city/{id}")   
   public ResponseEntity<City> getCityById(@PathVariable(name = "id") Integer id) {   
    City city = cityService.getCityById(id);   
    return new ResponseEntity<>(city, HttpStatus.OK);   
   }   
  }   

If we run the application on port 8080, then executing the below endpoint, we can observe from the logs that the query is executed only once.

http://localhost:8080/city/53

Logs:

 Hibernate:   
   select  
     city0_.ID as ID1_0_0_,  
     city0_.CountryCode as CountryC2_0_0_,  
     city0_.District as District3_0_0_,  
     city0_.Name as Name4_0_0_,  
     city0_.Population as Populati5_0_0_   
   from  
     city city0_   
   where  
     city0_.ID=?  

You can set the below properties in application.properties to enable SQL logs.

spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true

Removing Objects from First Level Cache

We can clear the cache of all objects using the clear() method of the Hibernate session class. Let's update the CityService.java class.

1:  package com.devtalkers.jpacache.service;  
2:  import com.devtalkers.jpacache.entity.City;  
3:  import com.devtalkers.jpacache.repository.CityRepository;  
4:  import org.hibernate.Session;  
5:  import org.springframework.beans.factory.annotation.Autowired;  
6:  import org.springframework.stereotype.Service;  
7:    
8:  import javax.persistence.EntityManager;  
9:  import javax.transaction.Transactional;  
10:  @Service  
11:  public class CityService {  
12:    
13:    @Autowired  
14:    private CityRepository cityRepository;  
15:    
16:    @Autowired  
17:    private EntityManager entityManager;  
18:    
19:    @Transactional  
20:    public City getCityById(Integer id){  
21:      City city = cityRepository.getOne(id);//get city by id  
22:      System.out.println(city);  
23:      Session session = entityManager.unwrap(Session.class);  
24:      session.clear();  
25:      city = cityRepository.getOne(id);//get city by id again  
26:      return city;  
27:    }  
28:  }  

At line 17, we have Autowired the EntityManager class, then at line 23, we get the current session using the unwrap method of the EntityManager class. Finally, we clear the session using clear() method of the Hibernate Session class. Now if you hit the URL http://localhost:8080/city/53 again, you can observe that query is being executed more than one time.

Logs 

 Hibernate:   
   select  
     city0_.ID as ID1_0_0_,  
     city0_.CountryCode as CountryC2_0_0_,  
     city0_.District as District3_0_0_,  
     city0_.Name as Name4_0_0_,  
     city0_.Population as Populati5_0_0_   
   from  
     city city0_   
   where  
     city0_.ID=?  
 City{id=53, name='Tafuna AZXC', countryCode='ASM', district='Tutuila', population=5200}  
 Hibernate:   
   select  
     city0_.ID as ID1_0_0_,  
     city0_.CountryCode as CountryC2_0_0_,  
     city0_.District as District3_0_0_,  
     city0_.Name as Name4_0_0_,  
     city0_.Population as Populati5_0_0_   
   from  
     city city0_   
   where  
     city0_.ID=?  

Instead of clearing the cache of all objects, we can also detach the particular object from the session cache using the evict method as below.

 session.evict(city);  

Conclusion

In this blog post, we have learned what is Hibernate First level Cache, then we saw an example of First level cache. In the end, we also saw how we can clear the cache of the object using the Hibernate session class.


No comments:

Powered by Blogger.