Spring Boot Cache Implementation for Database Operations

Alexander Ang
7 min readDec 2, 2019

Cache implementation was done using built-in cache function on spring boot framework.

First, you need to add the following maven dependency:

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>

To enable caching, add @EnableCaching annotation on any configuration class, for example the main class (in this case the DemoApplication class).

Other way of enabling cache is by using XML configuration:

<beans>
<cache:annotation-driven />

<bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
<property name="caches">
<set>
<bean
class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean"
name="addresses"/>
</set>
</property>
</bean>
</beans>

Caching with spring boot is much simpler by using annotations:

@Cacheable

@CacheEvict

@CachePut

@Caching

@CacheConfig

Conditional caching elements: condition, key, unless

  • @CacheEvict(value=”cacheName”, condition=#customer.name == ‘Herianto’”) → delete cache entry with the name “cacheName” if the customer name is “Herianto”
  • @Cacheable(value = “addresses”, key = “#customer.name”) → save method result of customer name with cache name of “addresses”
  • @CachePut(value=”addresses”, unless=”#result.length() < 64") → update cache entry with the name “addresses” except/unless result length is less than 64
  • Unlike “condition”, “unless” is evaluated after the method has been called and can therefore refer to the result

To implement cache, we create a simple project with the following classes and configuration:

PeopleController

  • /all : select all people on DB
  • /id : select people by id
  • /update: insert or update (if request has id) to people table
  • /flush: delete all cache

PeopleService

  • findAll() : select all people and save result to cache “findAllCache”
  • savePeople() : save or insert people and delete all cache
  • findById() : select people by id and save result to cache “findByIdCache”
  • flushCache() : delete all cache

PeopleRepository

People (entity)

PeopleDto

PeopleMapper

  • Mapper class by using mapstruct annotation @Mapper

Add @EnableCaching annotation in main class

application.properties

Data example in database. Table name: people.

Cache implementation is simulated using API hit steps (with system log below separated by “ — — — — — — “):

  1. /all (no cache, hit DB)
  2. /all (cached, doesn’t hit DB)
  3. /id (no cache)
  4. /id (cached)
  5. /flush (delete all cache)
  6. /id (no cache)
  7. /insert (insert new people, delete cache)
  8. /insert (update people, delete cache)
  9. /all (no cache)
2019-11-29 16:04:57.790 TRACE 2340 --- [nio-8080-exec-9] o.s.cache.interceptor.CacheInterceptor   : Computed cache key 'SimpleKey []' for operation Builder[public java.util.List com.example.demo.service.PeopleService.findAll()] caches=[findAllCache] | key='' | keyGenerator='' | cacheManager='' | cacheResolver='' | condition='' | unless='' | sync='false'2019-11-29 16:04:57.791 TRACE 2340 --- [nio-8080-exec-9] o.s.cache.interceptor.CacheInterceptor   : No cache entry for key 'SimpleKey []' in cache(s) [findAllCache]2019-11-29 16:04:57.791 TRACE 2340 --- [nio-8080-exec-9] o.s.cache.interceptor.CacheInterceptor   : Computed cache key 'SimpleKey []' for operation Builder[public java.util.List com.example.demo.service.PeopleService.findAll()] caches=[findAllCache] | key='' | keyGenerator='' | cacheManager='' | cacheResolver='' | condition='' | unless='' | sync='false'2019-11-29 16:04:57.791  INFO 2340 --- [nio-8080-exec-9] com.example.demo.service.PeopleService   : Connecting to DB...
— — — — — —
2019-11-29 16:05:00.812 TRACE 2340 --- [nio-8080-exec-1] o.s.cache.interceptor.CacheInterceptor : Computed cache key 'SimpleKey []' for operation Builder[public java.util.List com.example.demo.service.PeopleService.findAll()] caches=[findAllCache] | key='' | keyGenerator='' | cacheManager='' | cacheResolver='' | condition='' | unless='' | sync='false'2019-11-29 16:05:00.812 TRACE 2340 --- [nio-8080-exec-1] o.s.cache.interceptor.CacheInterceptor : Cache entry for key 'SimpleKey []' found in cache 'findAllCache'
— — — — — —
2019-11-29 16:05:21.767 TRACE 2340 --- [nio-8080-exec-2] o.s.cache.interceptor.CacheInterceptor : Computed cache key '23' for operation Builder[public com.example.demo.dto.PeopleDto com.example.demo.service.PeopleService.findById(java.lang.Long)] caches=[findByIdCache] | key='' | keyGenerator='' | cacheManager='' | cacheResolver='' | condition='' | unless='' | sync='false'2019-11-29 16:05:21.768 TRACE 2340 --- [nio-8080-exec-2] o.s.cache.interceptor.CacheInterceptor : No cache entry for key '23' in cache(s) [findByIdCache]2019-11-29 16:05:21.768 TRACE 2340 --- [nio-8080-exec-2] o.s.cache.interceptor.CacheInterceptor : Computed cache key '23' for operation Builder[public com.example.demo.dto.PeopleDto com.example.demo.service.PeopleService.findById(java.lang.Long)] caches=[findByIdCache] | key='' | keyGenerator='' | cacheManager='' | cacheResolver='' | condition='' | unless='' | sync='false'2019-11-29 16:05:21.768 INFO 2340 --- [nio-8080-exec-9] com.example.demo.service.PeopleService : Connecting to DB...
— — — — — —
2019-11-29 16:05:25.476 TRACE 2340 --- [nio-8080-exec-3] o.s.cache.interceptor.CacheInterceptor : Computed cache key '23' for operation Builder[public com.example.demo.dto.PeopleDto com.example.demo.service.PeopleService.findById(java.lang.Long)] caches=[findByIdCache] | key='' | keyGenerator='' | cacheManager='' | cacheResolver='' | condition='' | unless='' | sync='false'2019-11-29 16:05:25.476 TRACE 2340 --- [nio-8080-exec-3] o.s.cache.interceptor.CacheInterceptor : Cache entry for key '23' found in cache 'findByIdCache'
— — — — — —
2019-11-29 16:05:29.145 INFO 2340 --- [nio-8080-exec-4] com.example.demo.service.PeopleService : Flushing cache with name: findAllCache2019-11-29 16:05:29.146 INFO 2340 --- [nio-8080-exec-4] com.example.demo.service.PeopleService : Flushing cache with name: findByIdCache
— — — — — —
2019-11-29 16:05:31.657 TRACE 2340 --- [nio-8080-exec-5] o.s.cache.interceptor.CacheInterceptor : Computed cache key '23' for operation Builder[public com.example.demo.dto.PeopleDto com.example.demo.service.PeopleService.findById(java.lang.Long)] caches=[findByIdCache] | key='' | keyGenerator='' | cacheManager='' | cacheResolver='' | condition='' | unless='' | sync='false'2019-11-29 16:05:31.657 TRACE 2340 --- [nio-8080-exec-5] o.s.cache.interceptor.CacheInterceptor : No cache entry for key '23' in cache(s) [findByIdCache]2019-11-29 16:05:31.657 TRACE 2340 --- [nio-8080-exec-5] o.s.cache.interceptor.CacheInterceptor : Computed cache key '23' for operation Builder[public com.example.demo.dto.PeopleDto com.example.demo.service.PeopleService.findById(java.lang.Long)] caches=[findByIdCache] | key='' | keyGenerator='' | cacheManager='' | cacheResolver='' | condition='' | unless='' | sync='false'
— — — — — —
2019-11-29 16:05:37.333 INFO 2340 --- [nio-8080-exec-6] com.example.demo.service.PeopleService : Inserting new people...2019-11-29 16:05:37.348 TRACE 2340 --- [nio-8080-exec-6] o.s.cache.interceptor.CacheInterceptor : Invalidating entire cache for operation Builder[public com.example.demo.dto.PeopleDto com.example.demo.service.PeopleService.savePeople(com.example.demo.dto.PeopleDto)] caches=[findAllCache] | key='' | keyGenerator='' | cacheManager='' | cacheResolver='' | condition='',true,false on method public com.example.demo.dto.PeopleDto com.example.demo.service.PeopleService.savePeople(com.example.demo.dto.PeopleDto)2019-11-29 16:05:37.348 TRACE 2340 --- [nio-8080-exec-6] o.s.cache.interceptor.CacheInterceptor : Invalidating entire cache for operation Builder[public com.example.demo.dto.PeopleDto com.example.demo.service.PeopleService.savePeople(com.example.demo.dto.PeopleDto)] caches=[findByIdCache] | key='' | keyGenerator='' | cacheManager='' | cacheResolver='' | condition='',true,false on method public com.example.demo.dto.PeopleDto com.example.demo.service.PeopleService.savePeople(com.example.demo.dto.PeopleDto)
— — — — — —
2019-11-29 16:05:49.041 INFO 2340 --- [nio-8080-exec-7] com.example.demo.service.PeopleService : Updating people...2019-11-29 16:05:49.149 TRACE 2340 --- [nio-8080-exec-7] o.s.cache.interceptor.CacheInterceptor : Invalidating entire cache for operation Builder[public com.example.demo.dto.PeopleDto com.example.demo.service.PeopleService.savePeople(com.example.demo.dto.PeopleDto)] caches=[findAllCache] | key='' | keyGenerator='' | cacheManager='' | cacheResolver='' | condition='',true,false on method public com.example.demo.dto.PeopleDto com.example.demo.service.PeopleService.savePeople(com.example.demo.dto.PeopleDto)2019-11-29 16:05:49.150 TRACE 2340 --- [nio-8080-exec-7] o.s.cache.interceptor.CacheInterceptor : Invalidating entire cache for operation Builder[public com.example.demo.dto.PeopleDto com.example.demo.service.PeopleService.savePeople(com.example.demo.dto.PeopleDto)] caches=[findByIdCache] | key='' | keyGenerator='' | cacheManager='' | cacheResolver='' | condition='',true,false on method public com.example.demo.dto.PeopleDto com.example.demo.service.PeopleService.savePeople(com.example.demo.dto.PeopleDto)
— — — — — —
2019-11-29 16:05:54.770 TRACE 2340 --- [nio-8080-exec-8] o.s.cache.interceptor.CacheInterceptor : Computed cache key 'SimpleKey []' for operation Builder[public java.util.List com.example.demo.service.PeopleService.findAll()] caches=[findAllCache] | key='' | keyGenerator='' | cacheManager='' | cacheResolver='' | condition='' | unless='' | sync='false'2019-11-29 16:05:54.770 TRACE 2340 --- [nio-8080-exec-8] o.s.cache.interceptor.CacheInterceptor : No cache entry for key 'SimpleKey []' in cache(s) [findAllCache]2019-11-29 16:05:54.770 TRACE 2340 --- [nio-8080-exec-8] o.s.cache.interceptor.CacheInterceptor : Computed cache key 'SimpleKey []' for operation Builder[public java.util.List com.example.demo.service.PeopleService.findAll()] caches=[findAllCache] | key='' | keyGenerator='' | cacheManager='' | cacheResolver='' | condition='' | unless='' | sync='false'2019-11-29 16:05:54.770 INFO 2340 --- [nio-8080-exec-8] com.example.demo.service.PeopleService : Connecting to DB...

You can download the source code here:

https://github.com/plankrun/learn-spring-boot-cache

References

https://howtodoinjava.com/spring-boot2/spring-boot-cache-example/

https://dzone.com/articles/using-cache-in-spring-boot

https://www.baeldung.com/spring-cache-tutorial

https://medium.com/@ihorkosandiak/spring-boot-caching-d74591abe117

https://medium.com/@buddhiprabhath/spring-boot-crud-example-with-caching-c2c9b7fd8f05

https://www.baeldung.com/spring-boot-evict-cache

https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/cache/annotation/Cacheable.html

https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/cache/annotation/CacheEvict.html

https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/cache/annotation/CachePut.html

https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/cache/annotation/Caching.html

https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/cache/annotation/CacheConfig.html

https://stackoverflow.com/questions/12113725/how-do-i-tell-spring-cache-not-to-cache-null-value-in-cacheable-annotation

https://docs.spring.io/spring/docs/3.2.x/javadoc-api/org/springframework/cache/annotation/Cacheable.html

https://stackoverflow.com/questions/12113725/how-do-i-tell-spring-cache-not-to-cache-null-value-in-cacheable-annotation

https://docs.spring.io/spring/docs/3.2.x/javadoc-api/org/springframework/cache/annotation/Cacheable.html

--

--