javaspringspring-bootspring-cachecaffeine-cache

Caching in Spring Boot with Caffeine


I have this configuration class:

@Configuration
@EnableCaching
public class CacheConfig {

    @Bean
    @Primary
    public CacheManager cacheManager() {
        CaffeineCacheManager cacheManager = new CaffeineCacheManager("dogsInHouse");
        cacheManager.setCaffeine(Caffeine.newBuilder()
                .initialCapacity(200)
                .expireAfterAccess(Duration.ofDays(30))
                .maximumSize(500));
        return cacheManager;
    }
}

in the properties file:

spring.jpa.show-sql=true

in the service:

@Service
@Transactional(readOnly = true)
@Slf4j
@CacheConfig(cacheNames = {"dogsInHouse"})
public class DogsInHouseService {

    @Cacheable("dogsInHouse")
    public DogsInHouse findDogHouseEnFromDB (String key) {
        return dogsEnRepository.findByNameAndLangIs(key);
    }
}

The only thing that I can always see in the console is select query but I don't see the cache logs.


Solution

  • Use logging.level.org.springframework.cache=TRACE in your application.properties to see whether or not the value is picked from the cache.

    NOTE: When you hit the service for the first time, it will bring data from database and will put the data into the cache and next time, it will bring from the cache.

    Please remove @CacheConfig you don't need that in Spring Boot App.


    I've used spring-data-jpa with MySQL for demonstration.

    Same Config used. Just added for reference.

    @Configuration
    @EnableCaching
    public class CacheConfig {
    
        @Bean
        @Primary
        public CacheManager cacheManager() {
            CaffeineCacheManager cacheManager = new CaffeineCacheManager("dogsInHouse");
            cacheManager.setCaffeine(Caffeine.newBuilder()
                    .initialCapacity(200)
                    .expireAfterAccess(Duration.ofDays(30))
                    .maximumSize(500));
            return cacheManager;
        }
    }
    

    DogInHouse for testing:

    @Entity
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class DogInHouse {
    
        @Id
        @GeneratedValue
        private int id;
    
        @Column(name = "name")
        private String name;
    
    }
    

    DogInHouseRepository for testing:

    public interface DogInHouseRepository extends JpaRepository<DogInHouse, Integer> {
    }
    

    My Service class for testing:

    @Service
    @Slf4j
    public class DogsInHouseService {
    
        @Autowired
        private DogInHouseRepository dogsEnRepository;
    
        @Cacheable(value = "dogsInHouse")
        public Optional<DogInHouse> findDogHouseById(int key) {
            return dogsEnRepository.findById(key);
        }
    
    }
    

    application.properties for testing:

    spring.datasource.url=jdbc:mysql://localhost:3306/test
    spring.datasource.username=root
    spring.datasource.password=Anish@123
    spring.jpa.hibernate.ddl-auto=create-drop
    spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQLDialect
    spring.jpa.properties.hibernate.format_sql=true
    logging.level.org.hibernate.SQL=DEBUG
    logging.level.org.springframework.cache=TRACE
    

    My pom.xml had these dependencies:

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>
        <dependency>
             <groupId>com.github.ben-manes.caffeine</groupId>
             <artifactId>caffeine</artifactId>
        </dependency>
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
        </dependency>
    </dependencies>
    

    My Application Log:

    First Time Service Hit:

    2023-10-21T22:22:16.575+05:30 TRACE 14989 --- [nio-8080-exec-3] o.s.cache.interceptor.CacheInterceptor   : Computed cache key '1' for operation Builder[public java.util.Optional com.example.springbootmysql.DogsInHouseService.findDogHouseById(int)] caches=[dogsInHouse] | key='' | keyGenerator='' | cacheManager='' | cacheResolver='' | condition='' | unless='' | sync='false'
    2023-10-21T22:22:16.576+05:30 TRACE 14989 --- [nio-8080-exec-3] o.s.cache.interceptor.CacheInterceptor   : No cache entry for key '1' in cache(s) [dogsInHouse]
    2023-10-21T22:22:16.577+05:30 TRACE 14989 --- [nio-8080-exec-3] o.s.cache.interceptor.CacheInterceptor   : Computed cache key '1' for operation Builder[public java.util.Optional com.example.springbootmysql.DogsInHouseService.findDogHouseById(int)] caches=[dogsInHouse] | key='' | keyGenerator='' | cacheManager='' | cacheResolver='' | condition='' | unless='' | sync='false'
    2023-10-21T22:22:16.587+05:30 DEBUG 14989 --- [nio-8080-exec-3] org.hibernate.SQL                        : 
        select
            d1_0.id,
            d1_0.name 
        from
            dog_in_house d1_0 
        where
            d1_0.id=?
    

    Currently there is no value in the cache with the key that is passed, So, a select query will be executed via Hibernate to bring the value from DB and the value is put into the cache by that key.

    Second Time Service Hit:

    2023-10-21T22:23:33.485+05:30 TRACE 14989 --- [nio-8080-exec-6] o.s.cache.interceptor.CacheInterceptor   : Computed cache key '1' for operation Builder[public java.util.Optional com.example.springbootmysql.DogsInHouseService.findDogHouseById(int)] caches=[dogsInHouse] | key='' | keyGenerator='' | cacheManager='' | cacheResolver='' | condition='' | unless='' | sync='false'
    2023-10-21T22:23:33.490+05:30 TRACE 14989 --- [nio-8080-exec-6] o.s.cache.interceptor.CacheInterceptor   : Cache entry for key '1' found in cache 'dogsInHouse'
    

    This time the value exists in the cache by the same key that was passed earlier and the value is fetched from the cache.

    Postman Output:

    enter image description here