14 October 2018

Caching with Spring Boot

What-Why Cache?
Cache is a memory between an application and database to reduce the number of
Database hits.It improves performance of applications by reducing expensive
operations(Ex:DB hits), if it is stored with frequently accessed data.
Data which changes less frequently(static master data) and frequently accessed
data can be stored in cache.


Spring boot Cache:
Spring framework provides cache
api to integrate with different cache providers.
Spring boot provides @EnableCaching annotation to enable cache management.
When @Cacheable annotation is used at method level, Spring manages the request and response in cache.@Cacheable annotation has "key" attribute to decide a request to be cached.

Example:
Check code at https://github.com/java9pro/java9pro-cache
Project structure look like


pom.xml
Add dependency spring-boot-starter-cache for enabling cache management.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"         
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"         
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>java9pro</groupId>
    <artifactId>java9pro.springboot.cache</artifactId>
    <version>1.0-SNAPSHOT</version>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.13.RELEASE</version>
    </parent>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>
        <dependency>
            <groupId>org.codehaus.plexus</groupId>
            <artifactId>plexus-archiver</artifactId>
            <version>3.4</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.7.0</version>
                <configuration>
                    <source>1.9</source>
                    <target>1.9</target>
                    <jdkToolchain>
                        <version>9</version>
                    </jdkToolchain>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <version>3.0.2</version>
            </plugin>
        </plugins>
    </build>

</project>
ProductController
It has request mapping with url path parameter as "id" and calls service method.
package com.cache.java9pro;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@RestControllerpublic class ProductController {
    @Autowired    MobileService mobileService;

    @GetMapping("/mobile/{id}")
    public   Mobile findMobileDetailById(@PathVariable String id)
    {
       return mobileService.getMobileDetailByID(id);
    }
}
Mobile
It is a simple POJO with required attribute id
package com.cache.java9pro;

public class Mobile {
    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getModel() {
        return model;
    }

    public void setModel(String model) {
        this.model = model;
    }

    public String getRam() {
        return ram;
    }

    public void setRam(String ram) {
        this.ram = ram;
    }

    String id;
    String model;
    String ram;
    public Mobile(String id, String model, String ram) {
        this.id=id;
        this.model=model;
        this.ram=ram;
    }
}
MobileService
It has @Cacheable annotation enabled method  getMobileDetailByID 
which call DAO method if and only if the request/response are not available in cache. 
package com.cache.java9pro;

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

@Servicepublic class MobileService {

    @Autowired    MobileDAO  mobileDAO;

    @Cacheable(value="mobile", key="#id")
    public Mobile getMobileDetailByID(String id)
    {
        Mobile mobile=null;
        try {
            System.out.println("Getting from database");
            Thread.sleep(5000);
            mobile = mobileDAO.getMobileInfo(id);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return mobile;
    }
}

MobileDAO
To simulate database access, provided switch-case implementation for different ids
package com.cache.java9pro;

import org.springframework.stereotype.Component;

@Componentpublic class MobileDAO {

    public Mobile getMobileInfo(String id) {
        switch (id){
            case "1" : return new Mobile(id,"Samsung","4GB");
            case "2" : return new Mobile(id,"Apple","6GB");
            case "3" : return new Mobile(id,"Motorola","2GB");
            case "4" : return new Mobile(id,"Lenovo","3GB");

        }
        return null;
    }
}
CacheApplication

It is Spring boot application method with annotation @EnableCaching

package com.cache.java9pro;

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

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

application.properties
application.port=8080
Upon running main method application starts on port 8080.
To test the application execute the following URLs
  1. http://localhost:8080/mobile/1
  2. http://localhost:8080/mobile/2
  3. http://localhost:8080/mobile/3
  4. http://localhost:8080/mobile/4
  5. http://localhost:8080/mobile/1
  6. http://localhost:8080/mobile/2
  7. http://localhost:8080/mobile/3
  8. http://localhost:8080/mobile/4
First 4 URLs will access DAO method.
Last 4 URLs (5,6,7,8) will not access DAO method and
will provide response from cache.
Sample output looks like







No comments: