Spring Cache (Redis)
====================
https://github.com/spring-boot-tutorials/spring-cache-redis
In this article we will use Redis as a cache for a Spring Boot application.
`Spring Boot supported cache providers `_:
- Generic
- JCache (JSR-107) (EhCache 3, Hazelcast, Infinispan, and others)
- Hazelcast
- Infinispan
- Couchbase
- Redis
- Caffeine
- Cache2k
- Simple
Install & Run Redis Server
--------------------------
.. code-block:: sh
docker run -p 6379:6379 redis
Create Initial Code Base
------------------------
- Go to https://start.spring.io/
- Add the following dependencies:
- spring-boot-starter-cache
- spring-boot-starter-data-redis
- lombok
- Click `Generate`
Dependencies
------------
Dependencies used in ``pom.xml``
.. code-block:: xml
org.springframework.boot
spring-boot-starter-cache
org.springframework.boot
spring-boot-starter-data-redis
org.projectlombok
lombok
true
Properties
----------
Add the following properties into ``src/main/resources/application.properties``:
.. code-block:: properties
spring.data.redis.host=localhost
spring.data.redis.port=6379
Configuration
-------------
Let's create a new file ``src/main/java/com/example/spring_cache_abstraction/DefaultConfig.java``
.. code-block:: java
@Configuration
@EnableCaching
public class DefaultConfig {
public static final String CACHE_ADDRESS = "addresses";
public static final String CACHE_DIRECTORY = "directory";
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {
RedisCacheConfiguration cacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(10)) // Set default TTL to 10 minutes
.disableCachingNullValues(); // Don't cache null values
return RedisCacheManager.builder(connectionFactory)
.cacheDefaults(cacheConfiguration)
.initialCacheNames(Set.of(CACHE_ADDRESS, CACHE_DIRECTORY))
.build();
}
}
Model
-----
Let's create a new POJO ``src/main/java/com/example/spring_cache_abstraction/Customer.java``
.. code-block:: java
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@SuperBuilder
public class Customer {
private String name;
}
Service
-------
Let's create a service class ``src/main/java/com/example/spring_cache_abstraction/DefaultService.java``:
.. code-block:: java
@Service
public class DefaultService {
///////////////
// CACHEABLE //
///////////////
@Cacheable(value = CACHE_ADDRESS, key = "#customer.name")
public String getCachedAddress(Customer customer) {
System.out.println("inside cacheable function `getCachedAddress`");
return "address + " + customer.getName();
}
@Cacheable(value = CACHE_DIRECTORY, key = "#customer.name")
public String getCachedDirectory(Customer customer) {
System.out.println("inside cacheable function `getCachedDirectory`");
return "address + " + customer.getName();
}
/////////////////
// CACHE EVICT //
/////////////////
@CacheEvict(value = CACHE_ADDRESS, allEntries = true)
public void evictAllAddress() {}
@CacheEvict(value = CACHE_ADDRESS, key = "#customer.name")
public void evictSingleAddress(Customer customer) {}
///////////////
// CACHE PUT //
///////////////
// @CachePut is used to ensure that a method is always executed
// and its result is then placed into the cache. Unlike @Cacheable,
// @CachePut does not skip method execution if a value is already
// present in the cache. Instead, it forces the method to run, and the
// result of that execution is used to update the corresponding entry
// in the cache. This is useful for scenarios where you need to guarantee
// that the cache always reflects the latest state of data, such as
// after an update operation.
@CachePut(value = CACHE_ADDRESS, key = "#customer.name")
public String getAddressAndCache(Customer customer) {
System.out.println("inside cacheable function `getAddressAndCache`");
return "address + " + customer.getName();
}
/////////////
// CACHING //
/////////////
@Caching(evict = {
@CacheEvict(value = CACHE_ADDRESS, allEntries = true),
@CacheEvict(value = CACHE_DIRECTORY, allEntries = true),
})
public void evictAllAddressAndDirectory() {}
@Caching(
cacheable = {
@Cacheable(CACHE_ADDRESS),
@Cacheable(value = CACHE_DIRECTORY)},
put = {
@CachePut(CACHE_ADDRESS),
@CachePut(CACHE_DIRECTORY),}
)
public void unintelligible() {}
/////////////////////////
// Conditional Caching //
/////////////////////////
@CachePut(value="addresses", condition="#customer.name=='Tom'")
public String getAddressConditional1(Customer customer) {
return null;
}
@CachePut(value="addresses", unless="#result.length()<64")
public String getAddressConditional2(Customer customer) {
return null;
}
}
Main
----
Now let's use this service.
Go back to ``MainApplication.java`` and add the following:
.. code-block:: java
@SpringBootApplication
public class SpringCacheAbstractionApplication implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(SpringCacheAbstractionApplication.class, args);
}
@Autowired
DefaultService defaultService;
@Override
public void run(String... args) {
List customers = List.of(
new Customer("marcus"),
new Customer("jesus"),
new Customer("marcus"),
new Customer("jesus"),
new Customer("asher"));
System.out.println("\nSTART ADDRESSES");
customers.forEach(c -> {
System.out.println("Invoking cacheable function `getCachedAddress` for input param=" + c);
defaultService.getCachedAddress(c);
});
System.out.println("\nSTART DIRECTORY");
customers.forEach(c -> {
System.out.println("Invoking cacheable function `getCachedDirectory` for input param=" + c);
defaultService.getCachedDirectory(c);
});
}
}
Run Application
---------------
Open terminal at project root and execute the following:
.. code-block:: sh
mvn spring-boot:run
There should be no errors and verify the output.