You are here
Home > Redis >

How to implement Redis Cache in Spring Boot Application?

How to implement Redis Cache in Spring Boot Application?Many a time, we all come to a phase when our application does not perform well as it is expected to. Apart from several other solutions, we also look for a caching technique to make DB calls faster. In order to make it possible, we have Redis Cache technique in place. Redis cache helps us by minimizing the number of network calls while accessing the data from DB. Needless to say, our topic of discussion in this article is ‘How to implement Redis Cache in Spring Boot Application?’.

Spring Boot supports this feature via the dependency ‘Spring Data Redis’. In addition, we need to download Redis Server to make Redis Cache functional in the Spring Boot Application. Moreover, apart from Cache, Redis can also be used as a database and Message Broker. However, in a real time application, Redis is popular for a Cache Manager as compared to database & Message Broker. Let’s start discussing about our article ‘How to implement Redis Cache in Spring Boot Application?’ and its related concepts.

If you want to learn CRUD operation using Redis DB, please visit our internal article on ‘Spring Boot Redis CRUD Example‘.

What can you expect from this Article?

Once you go through the complete article, you will be able to answer the following questions:

1) What is Redis?
2) What is Redis Used for?
3) What is Redis Cache?
4) What is the advantage of using Redis Cache in your application?
5) How does the Redis Cache work in the Application?
6) What is Redis Database?
7) What is Redis Server?
8) How to download Redis Server?

9) What are the important annotations to enable Redis Cache in the Application?
10) When to use @Caching Annotation?
11) How to implement conditional caching using Annotations?
12) How to write a REST application in Spring Boot?
13) How to implement Redis Cache in Spring Boot Application?
14) How to test the application after implementing Redis Cache?
15) In the end, How to use Redis CLI and its commands?

What is Redis?

Redis is an open source (BSD licensed) in-memory remote data structure store (database) that offers high performance, replication, and a unique data model. The full form of Redis is Remote Directory Server. Moreover, we can use it in multiple forms. Redis provides data structures such as strings, hashes, lists, sets, sorted sets with range queries, bitmaps, hyperloglogs, geospatial indexes, and streams.

You can use Redis from most programming languages. Redis is written in ANSI C and works in most POSIX systems like Linux, *BSD, and OS X, without external dependencies. Linux and OS X are the two operating systems where Redis is developed and tested the most.

What is Redis Used for?

We can use Redis in the following forms.

1) In-Memory Database: As an In-Memory database, We will get some empty memory to perform database operations. Moreover, it acts as No-SQL database and there are No Tables, No Sequences, No Joins concept. We can store data in the form of String, Hash Operations***, List, Set etc. In-built services will be available.
2) Cache: We can also use Redis as a Cache to increase our application performance.
3) Message Broker(MQ): Another use of Redis is as a Message Broker.

In real time application, Redis is popular for a Cache Manager as compared to database & message broker. As a cache manager, it reduces network calls and improves the performance of an application.

What is Redis Cache?

Redis Cache is nothing but a Cache Management feature offered by Redis. Redis is normally used as a cache to store repeatedly accessed data in memory so that the user can feel the better performance of the application. The Redis Cache offers various features like how long you want to keep data, and which data to remove first, and some other bright caching models.

What is the advantage of using Redis Cache in your application?

Like any other Caching Technique, Redis Cache also minimizes the number of network calls made by your application, which in return improves  performance of the application as a whole. One request from an application to the DB is similar to one network call. We can also achieve the better scaling once we apply any caching mechanism in the application as the database can serve more calls in this case.

How does the Redis Cache work in the Application?

When we perform a DB retrieve operation via an Application, the Redis Cache stores the result in it’s cache. Further, when we perform the same retrieve operation, it returns the result from the cache itself and ignore the second call to database. Similarly, when we perform a DB update operation, the Redis Cache also updated the result in its cache. Needless to say, for delete operation also it deleted the data from the cache accordingly. In this way, there are no chances of getting incorrect data.

What is Redis Database?

Redis Database is an in-memory database that persists on disk. It means when we use Redis Database, we occupy a memory on the disk to use as a Database. The data model is key-value, but many several kind of values are supported such as Strings, Lists, Sets, Sorted Sets, Hashes, Streams, HyperLogLogs, Bitmaps etc.

What is Redis Server?

The full form of Redis is REmote DIctionary Server. When we use Redis in any form such as database, cache or Message Broker, we need to download a Redis Server in our system. People in the industry just call it Redis Server.

How to download Redis Server?

1) Visit any of the link below to download Redis on Windows
Download Redis Server  :  version 3.2.100
Download Redis Server  :  version 5.0.10
2) Click on Redis-x64-5.0.10.zip and extract it to a folder
3) Under folder ‘Redis-x64-5.0.10’, you will find redis-server.exe
4) In order to start Redis Server, double click on ‘redis-server.exe’ to start Redis Server

What are the important annotations to enable Redis Cache in the Application?

Generally, there are four important annotations that we apply to implement Redis Cache feature in our application. They are as below:

@EnableCaching 

We apply this annotation at the main class (starter class) of our application in order to tell Spring Container that we need a caching feature in our application.

@Cacheable 

@Cacheable is used to fetch (retrieve) data from the DB to the application and store in Redis Cache. We apply it on the methods that get (retrieve) data from DB. @Cacheable requires a return value of the method that adds or updates data in the cache.

The @Cacheable annotation offers us to use attributes. For example, we can provide a cache name by using the value or cacheNames attribute. We can also define the key attribute of the annotation that uniquely identifies each entry in the cache. If we do not specify the key, Spring utilizes its default mechanism to create the key. Moreover, we can also apply a condition in the annotation by using the condition attribute.

@CachePut 

We use @CachePut in order to update data in the Redis Cache while there is any update of data in DB. We apply it on the methods that make modifications in DB.

@CacheEvict 

We use @CacheEvict in order to remove the data in the Redis Cache while there is any removal of data in DB. We apply it on the methods that delete data from DB. It can be used with void methods.

How to implement Redis Cache in Spring Boot Application?

In order to implement Redis Cache using Spring Boot, we need to create one small application that will have CRUD operations in place. Then we will apply Redis Cache feature in Retrieve, Update and Delete Operations. However, we should not apply Cache feature in a Create operation as we will not get any benefit of it. Let’s start working on ‘How to implement Redis Cache in Spring Boot?’ step by step as below:

Details of Use-case 

We will create a CRUD application using REST. Let’s assume an entity ‘Invoice’. For that our entity class is Invoice.java. In order to create a complete REST Application, we will have Controller, Service & Repository layers as per industry best practices. Once we complete developing the Invoice REST Application, we will further apply annotations on certain methods to get the benefits of Redis Cache. This is the step by step approach to implement the Redis Cache in our application. However, we are going to provide the complete code of each file.

Step#1: Create a new Spring Boot Starter Project using STS

Let’s create a Spring Boot Starter project using STS. While creating Starter Project select ‘Spring Web’, ‘Spring Data JPA’, ‘MySQL Driver’, ‘Spring Data Redis’, ‘Lombok’, and ‘Sprong Boot DevTools’ as starter project dependencies. Even If you don’t know how to create a Spring Boot Starter Project, Kindly visit our Internal Link on ‘How to create a Spring Boot Starter Project using STS?‘. Even, if you are new to ‘Lombok’, kindly visit ‘How to configure Lombok‘ and to know all about it in detail.

If you are adding the dependencies manually, below is the redis dependency needs to be added.

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

Step#2: Update application.properties

The application.properties will be as below:

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/rediscachetest
spring.datasource.username=root
spring.datasource.password=****

spring.jpa.database-platform=org.hibernate.dialect.MySQL8Dialect
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=update

spring.cache.type=redis
spring.cache.redis.cache-null-values=true
#spring.cache.redis.time-to-live=40000

Step#3: Add annotation @EnableCaching at starter class

The annotation @EnableCaching is required to get the benefits of Caching Feature.

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

@SpringBootApplication
@EnableCaching
public class RedisAsaCacheWithSpringBootApplication {

   public static void main(String[] args) {
      SpringApplication.run(RedisAsaCacheWithSpringBootApplication.class, args);
   }
}

Step#4: Create an Entity class as Invoice.java

Below code represents the entity class with Lombok Annotations on it. Additionally, make sure to create one serialVersionUID.

@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity
public class Invoice implements Serializable{

    private static final long serialVersionUID = -4439114469417994311L;

    @Id
    @GeneratedValue
    private Integer invId;
    private String invName;
    private Double invAmount;
}

Step#5: Create a Repository Interface as InvoiceRepository.java

Now, lets create a Repository Interface as InvoiceRepository.java which will extend JpaRepository<Invoice, Integer> as below.

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import com.dev.springboot.redis.model.Invoice;

@Repository
public interface InvoiceRepository extends JpaRepository<Invoice, Integer> {

}

Step#6: Create a Custom Exception class as InvoiceNotFoundException.java

This class is just to handle Exceptions in a proper way.

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

@ResponseStatus(HttpStatus.NOT_FOUND)
public class InvoiceNotFoundException extends RuntimeException {

   private static final long serialVersionUID = 7428051251365675318L;

   public InvoiceNotFoundException(String message) {
      super(message);
   }
}

Step#7 & 8: Create Service Interface & Impl class as InvoiceService.java & InvoiceServiceImpl.java

In order to develop a service layer, we will have a service interface & an implementation class of it as InvoiceService.java and InvoiceServiceImpl.java respectively as below. The InvoiceServiceImpl.java is the class where we need to keep our focus on. Moreover, this is the place where we apply Caching Mechanism offered by Redis.

Service Interface:

import com.dev.springboot.redis.model.Invoice;
import java.util.List;

public interface InvoiceService {

    public Invoice saveInvoice(Invoice inv);
    public Invoice updateInvoice(Invoice inv, Integer invId);
    public void deleteInvoice(Integer invId);
    public Invoice getOneInvoice(Integer invId);
    public List<Invoice> getAllInvoices();
}

Service Implementation Class:

import java.util.List;
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 com.dev.springboot.redis.exception.InvoiceNotFoundException;
import com.dev.springboot.redis.model.Invoice;
import com.dev.springboot.redis.repo.InvoiceRepository;

@Service
public class InvoiceServiceImpl implements InvoiceService {

    @Autowired
    private InvoiceRepository invoiceRepo;

    @Override
    public Invoice saveInvoice(Invoice inv) {

        return invoiceRepo.save(inv);
    }

    @Override
    @CachePut(value="Invoice", key="#invId")
    public Invoice updateInvoice(Invoice inv, Integer invId) {
       Invoice invoice = invoiceRepo.findById(invId)
            .orElseThrow(() -> new InvoiceNotFoundException("Invoice Not Found"));
       invoice.setInvAmount(inv.getInvAmount());
       invoice.setInvName(inv.getInvName());
       return invoiceRepo.save(invoice);
    }

    @Override
    @CacheEvict(value="Invoice", key="#invId")
    // @CacheEvict(value="Invoice", allEntries=true) //in case there are multiple records to delete
    public void deleteInvoice(Integer invId) {
       Invoice invoice = invoiceRepo.findById(invId)
           .orElseThrow(() -> new InvoiceNotFoundException("Invoice Not Found"));
       invoiceRepo.delete(invoice);
    }

    @Override
    @Cacheable(value="Invoice", key="#invId")
    public Invoice getOneInvoice(Integer invId) {
       Invoice invoice = invoiceRepo.findById(invId)
         .orElseThrow(() -> new InvoiceNotFoundException("Invoice Not Found"));
       return invoice;
    }

    @Override
    @Cacheable(value="Invoice")
    public List<Invoice> getAllInvoices() {
       return invoiceRepo.findAll();
    }
}

Step#9: Create RestController class as InvoiceRestController.java 

This RestController will have end-points to make REST calls and get results.

import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.dev.springboot.redis.model.Invoice;
import com.dev.springboot.redis.service.InvoiceService;

@RestController
@RequestMapping("/invoice")
public class InvoiceController {

    @Autowired
    InvoiceService invoiceService;

    @PostMapping("/saveInv")
    public Invoice saveInvoice(@RequestBody Invoice inv) {
       return invoiceService.saveInvoice(inv);
    }

    @GetMapping("/allInv") 
    public ResponseEntity<List<Invoice>> getAllInvoices(){
       return ResponseEntity.ok(invoiceService.getAllInvoices());
    }

    @GetMapping("/getOne/{id}")
    public Invoice getOneInvoice(@PathVariable Integer id) {
       return invoiceService.getOneInvoice(id);
    }

    @PutMapping("/modify/{id}")
    public Invoice updateInvoice(@RequestBody Invoice inv, @PathVariable Integer id) {
       return invoiceService.updateInvoice(inv, id);
    }

    @DeleteMapping("/delete/{id}")
    public String deleteInvoice(@PathVariable Integer id) {
       invoiceService.deleteInvoice(id);
       return "Employee with id: "+id+ " Deleted !";
    }
}

How to test the application after implementing Redis Cache?

1) Start Redis Server.
2) Start your Spring Boot Application.
3) Make a Rest Call using any REST client on operations which are get, update & delete. You can use any REST client such as any User Interface, RestTemplate or Postman etc. However, we have used Postman just to test the functionality.
4) When you call an operation for the first time, you should see a DB query in the console.
5) If you call the same operation for the second time, you should not see the DB query in the console. If it is, you have successfully applied the Redis Cache.

FAQ

When to Use @Caching Annotation?

Suppose we want to apply multiple caching annotations on a particular method, let’s try it with an example.

@CacheEvict("Invoice") 
@CacheEvict(value="Invoice", key="#invId")
public void deleteInvoice(Integer invId) {
    ....
}

The above code would have a compilation error, since Java does not allow multiple annotations of the same type to be declared for a given method. In this case we should use @Caching annotation. For example, below code demonstrates the concept.

@Caching(
  evict = {@CacheEvict("Invoice"), @CacheEvict(value="Invoice", key="#invId")
}) 
public void deleteInvoice(Integer invId) {
     ....
}

Similarly, if we have annotations of different type, we can group them using @Caching annotation for better readability. However, below code will not have any compilation error. For example,

@CacheEvict(value = "usersList", allEntries = true)
@CachePut(value = "user", key = "#user.getUserId()")
public User updateUser(User user) {
    ....
}

The above code can be converted as below:

@Caching(
   evict = {@CacheEvict(value = "usersList", allEntries = true)},
   put   = {@CachePut(value = "user", key = "#user.getUserId()")}
) 
public User updateUser(User user) {
   ....
}

How to Implement Conditional Caching using Annotations?

If we have some requirement when we need to cache data only on a particular condition, we can parameterize our annotation with two parameters: ‘condition’ and ‘unless’. They accept a SpEL expression and ensures that the results are cached based on evaluating that expression. This kind of conditional caching can be useful for managing large amount of results. For example, let’s observe one example from each parameter.

Below Example demonstrates the concept of ‘condition’ parameter.

@CachePut(value="invoices", condition="#invoice.amount>=2496") 
public String getInvoice(Invoice invoice) {
    ...
}

Now, let’s see the concept of ‘unless’ parameter from the example below:

@CachePut(value="invoices", unless="#result.length()<24") 
public String getInvoice(Invoice invoice) {
    ...
}

The above annotation would cache addresses unless they were shorter than 24 characters.

The ‘condition’ and ‘unless’ parameters can be used in conjunction with all the caching annotations.

How to use Redis CLI?

In order to start & use Redis CLI (Command Line Interface), follow the below steps:

1) Go to installation directory of Redis Server (‘Redis-x64-5.0.10’ in our case)

2) Under folder ‘Redis-x64-5.0.10’, you will find redis-cli.exe

3) In order to start Redis CLI, double click on ‘redis-cli.exe’

4) A new window will open with ‘127.0.0.1:6379>’

5) Type your commands to check the results.

How to use commands in Redis CLI?

1) To get all Keys: 127.0.0.1:6379> KEYS *

2) To set simple string key value pair: 127.0.0.1:6379>SET mykey “Hello\nWorld”

3) To get value : 127.0.0.1:6379>GET mykey

4) To remove all the keys of all the existing database:  127.0.0.1:6379> FLUSHALL

5) To delete all the keys of the DB # 4:  127.0.0.1:6379> -n 4 FLUSHDB

In order to know more about Redis CLI commands, kindly visit Redis CLI manual.

Conclusion

After going through all the theoretical & example part of ‘How to implement Redis Cache in Spring Boot Application?’, finally, we should be able to implement Redis Cache in a Spring Boot Application. Similarly, we expect from you to further extend these examples and implement them in your project accordingly. I hope you might be convinced by the article ‘How to implement Redis Cache in Spring Boot Application?’. In addition, If there is any update in the future, we will also update the article accordingly. Moreover, Feel free to provide your comments in the comments section below.

Leave a Reply


Top