SpringBoot Caffeine cache with PostgreSQL backend

Performance is a big problem in software development. And Caching is one solution to speed up system. In the tutorial, JavaSampleApproach will show you how to use SpringBoot Caffeine cache with PostGreSQL backend.

Related posts:
How to work with Spring Cache | Spring Boot
Couchbase – How to create Spring Cache Couchbase application with SpringBoot
SpringBoot Hazelcast cache with PostgreSQL backend

I. Technologies

– Java 1.8
– Maven 3.3.9
– Spring Tool Suite – Version 3.8.1.RELEASE
– Spring Boot: 1.5.6RELEASE
– Couchbase 4.6.2

II. SpringBoot Caffeine cache

What is Caffeine cache?

In the org.springframework.boot.autoconfigure.cache.CacheProperties, we can see:


@ConfigurationProperties(prefix = "spring.cache")
public class CacheProperties {
	 * Guava specific cache properties.
	public static class Guava {
		@DeprecatedConfigurationProperty(reason = "Caffeine will supersede the Guava support in Spring Boot 2.0", replacement = "spring.cache.caffeine.spec")
		public String getSpec() {
			return this.spec;

		public void setSpec(String spec) {
			this.spec = spec;


-> Caffeine is a Java 8 rewrite of Guava’s cache that supersede the Guava support.

In the tutorial, we use SpringBoot and Caffeine cache with PostGreSQL backend as below architecture:

Springboot Caffeine Cache - architecture

For work with SpringCache Caffeine, we need dependencies:



If Caffeine is present, spring-boot-starter-cache ‘Starter’ will auto-configure a CaffeineCacheManager. Caches can be created on startup using the spring.cache.cache-names. We can cutomize a cache via properties in order:
1. A cache spec defined by spring.cache.caffeine.spec
2. A com.github.benmanes.caffeine.cache.CaffeineSpec bean is defined
3. A com.github.benmanes.caffeine.cache.Caffeine bean is defined

More details, we need check out the sourcecode of com.github.benmanes.caffeine.cache.CaffeineSpec or


public final class CaffeineSpec {
  long maximumSize = UNSET_INT;
  long maximumWeight = UNSET_INT;

  long expireAfterAccessDuration = UNSET_INT;
  long expireAfterWriteDuration = UNSET_INT;
  long refreshAfterWriteDuration = UNSET_INT;

maximumSize: Specifies the maximum number of entries the cache may contain
maximumWeight: Specifies the maximum weight of entries the cache may contain
expireAfterAccessDuration: Specifies that each entry should be automatically removed from the cache once a fixed duration has elapsed after the entry’s creation, the most recent replacement of its value, or its last read.
expireAfterWriteDuration: Specifies that each entry should be automatically removed from the cache once a fixed duration has elapsed after the entry’s creation, or the most recent replacement of its value.
refreshAfterWriteDuration: Specifies that active entries are eligible for automatic refresh once a fixed duration has elapsed after the entry’s creation, or the most recent replacement of its value.


spring.cache.caffeine.spec: maximumSize=500, expireAfterAccess=86400s

III. Practice

In the tutorial, we create a SpringBoot project as below:

Springboot Caffeine Cache - project structure

Step to do:
– Create SpringBoot project
– Create data model
– Create Repository
– Implement Service on top of Cache
– Implement RestAPIs
– Implement CommandLineRunner to init data
– Configuration
– Run and check results

1. Create SpringBoot project

Using SpringToolSuite to create a SpringBoot project, then add dependencies:







2. Create data model

– Create a Customer model:

package com.javasampleapproach.caffeinecache.model;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Table(name = "customer")
public class Customer {
	private String id;
	@Column(name = "firstname")
	private String firstName;
	@Column(name = "lastname")
	private String lastName;
	public Customer(){
	public Customer(String id, String firstName, String lastName){
		this.id = id;
		this.firstName = firstName;
		this.lastName = lastName;
	public String getId() {
		return this.id;
	public void setId(String id) {
		this.id = id;
	public String getFirstName() {
		return this.firstName;
	public void setFirstName(String firstName) {
		this.firstName = firstName;
	public String getLastName() {
		return this.lastName;
	public void setLastName(String lastName) {
		this.lastName = lastName;
	public String toString() {
		return String.format("Customer[ id=%s, firstName=%s, lastName=%s]", this.id, this.firstName, this.lastName);

3. Create Repository

– Using CrudRepository, create a interface CustomerRepository:

package com.javasampleapproach.caffeinecache.repo;

import org.springframework.data.repository.CrudRepository;

import com.javasampleapproach.caffeinecache.model.Customer;

public interface CustomerRepository extends CrudRepository{

4. Implement Service on top of Cache

Implement a cache component CustomerCache as below:

package com.javasampleapproach.caffeinecache.service.cache;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Component;

import com.javasampleapproach.caffeinecache.model.Customer;
import com.javasampleapproach.caffeinecache.repo.CustomerRepository;

@CacheConfig(cacheNames = {"customer"})
public class CustomerCache {
	CustomerRepository customerRepository;
    public Customer getOnCache(String id){
    	System.out.println("############# Backend processing...");
    	// simulation the time for processing
    	try {
		} catch (InterruptedException e) {
    	return customerRepository.findOne(id);
    public Customer putOnCache(String firstName, String id){
		// find a customer in repository
		Customer cust = customerRepository.findOne(id);
		// modify above customer by first-name
		// save to database
        return customerRepository.save(cust);
    @CacheEvict(key = "#id")
    public void evict(String id){

Then implement CustomerServices service on top of CustomerCache:

package com.javasampleapproach.caffeinecache.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.javasampleapproach.caffeinecache.model.Customer;
import com.javasampleapproach.caffeinecache.service.cache.CustomerCache;
public class CustomerServices {
	CustomerCache customerCache;
    public Customer putCustomer(String firstName, String id){
        return customerCache.putOnCache(firstName, id);
    public Customer get(String id){
    	return customerCache.getOnCache(id);
    public void evict(String id){
5. Implement RestAPIs

Create a WebController with 3 APIs {/api/cachable, /api/cacheput, /api/cacheevict}:

package com.javasampleapproach.caffeinecache.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.javasampleapproach.caffeinecache.model.Customer;
import com.javasampleapproach.caffeinecache.service.CustomerServices;

public class WebController {
    CustomerServices service;
    public Customer get(@RequestParam("id")String id){
        return service.get(id);
    public String put(@RequestParam("firstname") String firstName, @RequestParam("id")String id){
        service.putCustomer(firstName, id);
        return "Done";
    public String evict(@RequestParam("id")String id){
        return "Done";
6. Implement CommandLineRunner to init data

In main class, use CustomerRepository and CommandLineRunner to init data:

package com.javasampleapproach.caffeinecache;

import java.util.Arrays;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;

import com.javasampleapproach.caffeinecache.model.Customer;
import com.javasampleapproach.caffeinecache.repo.CustomerRepository;

public class SpringBootCaffeineCacheApplication implements CommandLineRunner{

	CustomerRepository customerRepo;
	public static void main(String[] args) {
		SpringApplication.run(SpringBootCaffeineCacheApplication.class, args);

	public void run(String... arg0) throws Exception {
		// initial data to PostGreSQL database 
		customerRepo.save(Arrays.asList(new Customer("1", "Jack", "Smith"), 
											new Customer("2", "Adam", "Johnson")));

Note: @EnableCaching is used to enable caching.

7. Configuration

Open application.properties file, configure spring.cache.* and spring.datasource.*

spring.cache.caffeine.spec: maximumSize=500, expireAfterAccess=86400s

8. Run and check results

Build and Run the SpringBoot project as commandlines: {mvn clean install, mvn spring-boot:run}.

-> See PostgreSQL database:

Springboot Caffeine Cache - database initial

– Make request 1: http://localhost:8080/api/cachable?id=1

-> Results:
Service process slowly and,

Springboot Caffeine Cache - request 1

Server has displayed a text on console:

############# Backend processing...

– Make request 2: http://localhost:8080/api/cachable?id=1
Now the response is faster because Customer with id = 1 has been cached before, the application just get data from cache storage.

Springboot Caffeine Cache - request 1

– Make request 3: http://localhost:8080/api/cacheput?id=1&firstname=Peter
Message is returned on Browser:
Now customer with id=1 is modified: firstname=’Peter’, NOT ‘Jack’.

– Make request 4: http://localhost:8080/api/cachable?id=1
Response is faster BUT the result is difference from first request:

Springboot Caffeine Cache - request 4

– Make request 5 – Make a cache-evict request: http://localhost:8080/api/cacheevict?id=1
Browser displays:
Now customer with id=1 was evicted from cache storage.

– Make request 6: http://localhost:8080/api/cachable?id=1
Now the behavior of the browser is the same as the first request because customer with id=1 was evicted to cache, and the method under @Cachable is executed.
Service process slowly and result:

############# Backend processing...

Springboot Caffeine Cache - request 4

IV. Sourcecode


By grokonez | September 15, 2017.

Related Posts

Got Something To Say:

Your email address will not be published. Required fields are marked *