Swiftorial Logo
Home
Swift Lessons
Matchups
CodeSnaps
Tutorials
Career
Resources

Introduction to Spring Data Caching

Spring Data Caching provides a way to cache the results of expensive method calls and reuse them when the same inputs occur again, improving the performance of your application. This guide covers key concepts and steps for getting started with Spring Data Caching, including adding dependencies, enabling caching, defining cache configurations, and implementing caching in your application.

Key Concepts of Spring Data Caching

  • Caching: Storing the results of expensive method calls and reusing them when the same inputs occur again.
  • @EnableCaching: Annotation to enable Spring's annotation-driven cache management capability.
  • @Cacheable: Annotation to indicate that the result of a method should be cached.
  • @CacheEvict: Annotation to indicate that a cache entry should be evicted.
  • @CachePut: Annotation to indicate that a method's result should be cached and update an existing cache entry.

Adding Dependencies

Include the appropriate Spring Data dependency for your cache provider in your pom.xml file. For example, for EhCache:

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

Enabling Caching

Enable caching in your Spring Boot application by adding the @EnableCaching annotation to your main application class:

Example: Application.java

// Application.java
package com.example.myapp;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;

@SpringBootApplication
@EnableCaching
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

Defining Cache Configurations

Define your cache configurations in an XML or Java configuration file:

Example: ehcache.xml

<ehcache xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
    xsi:noNamespaceSchemaLocation='http://www.ehcache.org/ehcache.xsd'>
    <diskStore path='java.io.tmpdir'/>
    <defaultCache maxElementsInMemory='10000' eternal='false'
        timeToIdleSeconds='120' timeToLiveSeconds='120' overflowToDisk='true'/>
    <cache name='users'
        maxElementsInMemory='1000' eternal='false'
        timeToIdleSeconds='300' timeToLiveSeconds='600'
        overflowToDisk='true' diskPersistent='false'
        memoryStoreEvictionPolicy='LRU'/>
</ehcache>

Implementing Caching in Your Application

Use the @Cacheable, @CacheEvict, and @CachePut annotations in your service layer:

Example: UserService.java

// UserService.java
package com.example.myapp.service;

import com.example.myapp.model.User;
import com.example.myapp.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Optional;

@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

    @Cacheable(value = "users", key = "#id")
    public Optional findUserById(Long id) {
        return userRepository.findById(id);
    }

    @CachePut(value = "users", key = "#user.id")
    public User saveUser(User user) {
        return userRepository.save(user);
    }

    @CacheEvict(value = "users", key = "#id")
    public void deleteUser(Long id) {
        userRepository.deleteById(id);
    }

    @CacheEvict(value = "users", allEntries = true)
    public void evictAllCacheValues() {
        // This will remove all entries from the "users" cache.
    }
}

Testing Spring Data Caching

Test your Spring Data Caching setup to ensure it works as expected:

Example: UserServiceTests.java

// UserServiceTests.java
package com.example.myapp;

import com.example.myapp.model.User;
import com.example.myapp.repository.UserRepository;
import com.example.myapp.service.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.cache.CacheManager;

import java.util.Optional;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.*;

@SpringBootTest
public class UserServiceTests {

    @Autowired
    private UserService userService;

    @MockBean
    private UserRepository userRepository;

    @Autowired
    private CacheManager cacheManager;

    @Test
    public void testFindUserById() {
        User user = new User();
        user.setId(1L);
        user.setUsername("testuser");
        user.setPassword("password");

        when(userRepository.findById(1L)).thenReturn(Optional.of(user));

        // First call, should hit the repository
        Optional foundUser = userService.findUserById(1L);
        assertThat(foundUser).isNotEmpty();
        assertThat(foundUser.get().getUsername()).isEqualTo("testuser");
        verify(userRepository, times(1)).findById(1L);

        // Second call, should hit the cache
        foundUser = userService.findUserById(1L);
        assertThat(foundUser).isNotEmpty();
        assertThat(foundUser.get().getUsername()).isEqualTo("testuser");
        verify(userRepository, times(1)).findById(1L); // still 1 time
    }

    @Test
    public void testEvictCache() {
        userService.evictAllCacheValues();
        assertThat(cacheManager.getCache("users")).isNotNull();
        assertThat(cacheManager.getCache("users").get(1L)).isNull();
    }
}

Key Points

  • Caching: Storing the results of expensive method calls and reusing them when the same inputs occur again.
  • @EnableCaching: Annotation to enable Spring's annotation-driven cache management capability.
  • @Cacheable: Annotation to indicate that the result of a method should be cached.
  • @CacheEvict: Annotation to indicate that a cache entry should be evicted.
  • @CachePut: Annotation to indicate that a method's result should be cached and update an existing cache entry.
  • Include the appropriate Spring Data dependency for your cache provider in your pom.xml file.
  • Enable caching in your Spring Boot application by adding the @EnableCaching annotation to your main application class.
  • Define your cache configurations in an XML or Java configuration file.
  • Use the @Cacheable, @CacheEvict, and @CachePut annotations in your service layer.
  • Test your Spring Data Caching setup to ensure it works as expected.

Conclusion

Spring Data Caching provides a way to cache the results of expensive method calls and reuse them when the same inputs occur again, improving the performance of your application. By understanding and implementing caching using the appropriate annotations, you can effectively manage and optimize data retrieval in your Spring Boot application. Happy coding!