Home  >  Spring 5

Spring Data Redis Cache

By Arvind Rai, August 08, 2018
This page will walk through Spring Data Redis Cache example. Spring provides RedisCacheManager to get CacheManager backed by Redis. Connections are provided by LettuceConnectionFactory or JedisConnectionFactory. LettuceConnectionFactory gives Lettuce connections and JedisConnectionFactory gives Jedis connections. Spring Data Redis dependencies are resolved by spring-boot-starter-data-redis. Spring Boot 2.0 resolves lettuce-core dependency by default using spring-boot-starter-data-redis. To work with Jedis, we need to include jedis dependency in our build file.
Spring @EnableCaching enables Spring cache management capability in our application. It is annotated with @Configuration annotation in JavaConfig. @Cacheable indicates that the result of invoking method can be cached and once result is cached, next call to method execution is skipped and only cached result is served. @CachePut adds or updates cache but does not skip method execution. @CacheEvict evicts cache but does not skip method execution. @Caching is used to group multiple cache annotations.

Technologies Used

Find the technologies being used in our example.
1. Java 9
2. Spring 5.0.8.RELEASE
3. Spring Data 2.0.9.RELEASE
4. Spring Boot Starter 2.0.4.RELEASE
5. Maven 3.5.2
6. Gradle 4.3.1
7. MySQL 5.5
8. Eclipse Oxygen

Maven and Gradle

Find the Maven file used in the demo application.
pom.xml
<?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>com.concretepage</groupId>
	<artifactId>spring-demo</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>war</packaging>
	<name>Spring</name>
	<description>Spring Demo Project</description>
	<parent>
	    <groupId>org.springframework.boot</groupId>
  	    <artifactId>spring-boot-starter-parent</artifactId>
	    <version>2.0.4.RELEASE</version>
 	    <relativePath/>
	</parent>
	<properties>
	    <context.path>spring-app</context.path>
	    <java.version>9</java.version>
	</properties>
	<dependencies>
	  <dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-web</artifactId>
	  </dependency>
	  <dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-data-jpa</artifactId>
	  </dependency>
	  <dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-data-redis</artifactId>
	  </dependency>	  
	  <dependency>
		 <groupId>mysql</groupId>
		 <artifactId>mysql-connector-java</artifactId>
		 <version>6.0.5</version>
	  </dependency>	  	  	  
	  <dependency>
		 <groupId>org.apache.commons</groupId>
		 <artifactId>commons-dbcp2</artifactId>
		 <version>2.1.1</version>
	  </dependency>
	  <dependency>
		 <groupId>javax.xml.bind</groupId>
		 <artifactId>jaxb-api</artifactId>
		 <version>2.3.0</version>
	  </dependency>  	      	  	  	  
	  <dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-tomcat</artifactId>
		<scope>provided</scope>
	  </dependency>      
	</dependencies>
	<build>
	  <plugins>
	    <plugin>
		  <groupId>org.apache.maven.plugins</groupId>
		  <artifactId>maven-war-plugin</artifactId>
		  <version>3.2.0</version>
		  <configuration>
			 <warName>${context.path}</warName>
		  </configuration>
		</plugin>
	  </plugins>
	</build>
</project> 
If we want to use Gradle, we can use following Gradle file.
build.gradle
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'war'
war.archiveName 'spring-app.war'

sourceCompatibility = 9

repositories {
    mavenCentral()
}
dependencies {
    compile 'org.springframework.boot:spring-boot-starter-web:2.0.4.RELEASE'
    compile 'org.springframework.boot:spring-boot-starter-data-jpa:2.0.4.RELEASE'    
    compile 'org.springframework.boot:spring-boot-starter-data-redis:2.0.4.RELEASE'
    compile 'mysql:mysql-connector-java:6.0.5'
    compile 'org.apache.commons:commons-dbcp2:2.1.1'
    compile 'javax.xml.bind:jaxb-api:2.3.0'
    providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat:2.0.4.RELEASE'    
} 

Redis Connection Factory

To get connections for Redis server, Spring provides LettuceConnectionFactory and JedisConnectionFactory.
1. Using LettuceConnectionFactory
LettuceConnectionFactory creates Lettuce connections. It requires lettuce-core which is provided by Spring Boot 2.0 by default using the starter spring-boot-starter-data-redis. Find the LettuceConnectionFactory bean in JavaConfig.
@Bean
public LettuceConnectionFactory redisConnectionFactory() {
    RedisStandaloneConfiguration redisConf = new RedisStandaloneConfiguration();
    redisConf.setHostName("localhost");
    redisConf.setPort(6379);
    return new LettuceConnectionFactory(redisConf);
}  
Using RedisStandaloneConfiguration we configure Redis connections via Redis connection factory.
Find the XML configuration for LettuceConnectionFactory bean.
<bean id="redisConnectionFactory" 
      class="org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory"
      p:host-name="localhost" p:port="6379" /> 
2. Using JedisConnectionFactory
JedisConnectionFactory provides Jedis based connections. In Spring Boot 2.0, spring-boot-starter-data-redis provides Lettuce by default instead of Jedis. So to work with Jedis, we need to include Jedis dependency in our build file.
Using Maven
<dependency>
   <groupId>redis.clients</groupId>
   <artifactId>jedis</artifactId>
</dependency>  
Using Gradle
compile 'redis.clients:jedis:2.9.0'  
To get Jedis connections, we need to create JedisConnectionFactory bean in our JavaConfig.
@Bean
public JedisConnectionFactory redisConnectionFactory() {
    RedisStandaloneConfiguration redisConf = new RedisStandaloneConfiguration();
    redisConf.setHostName("localhost");
    redisConf.setPort(6379);	    
    return new JedisConnectionFactory(redisConf);
} 
Find the XML configuration for JedisConnectionFactory bean.
<bean id="redisConnectionFactory" 
      class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"
      p:host-name="localhost" p:port="6379" /> 

RedisCacheManager

RedisCacheManager is the CacheManager backed by Redis. It is instantiated using RedisCacheManager.create() or RedisCacheManager.builder() by passing connection factory. To use it we need to create RedisCacheManager bean in our JavaConfig.
@Bean
public RedisCacheManager cacheManager() {
    RedisCacheManager rcm = RedisCacheManager.create(redisConnectionFactory());
    rcm.setTransactionAware(true);
    return rcm;
} 
setTransactionAware method decides whether cache objects should be transaction-aware or not. The default value is false and when we set setTransactionAware(true), then cache put and evict operations are synchronized with ongoing Spring managed transactions. It means cache manager will put or evict caches only after commit of successful transactions.
Find the XML configuration for RedisCacheManager bean.
<bean id="cacheManager" class="org.springframework.data.redis.cache.RedisCacheManager" 
      factory-method="create" c:connection-factory-ref="redisConnectionFactory" 
      p:transaction-aware="true"/> 

Enable Caching

To enable cache abstraction in our application, Spring provides @EnableCaching annotation. @EnableCaching enables annotation-driven cache management capability. It is equivalent to XML <cache:annotation-driven/>. They are responsible to register required Spring components to enable annotation-driven cache management. @EnableCaching is annotated with @Configuration. Find the JavaConfig for Redis configuration annotated with @EnableCaching.
RedisConfig.java
@Configuration
@EnableCaching
@PropertySource("classpath:redis.properties")
public class RedisConfig {
   @Autowired
   private Environment env;	
	
   @Bean
   public LettuceConnectionFactory redisConnectionFactory() {
	RedisStandaloneConfiguration redisConf = new RedisStandaloneConfiguration();
	redisConf.setHostName(env.getProperty("redis.host"));
	redisConf.setPort(Integer.parseInt(env.getProperty("redis.port")));
	redisConf.setPassword(RedisPassword.of(env.getProperty("redis.password")));	    
        return new LettuceConnectionFactory(redisConf);
   }
   @Bean
   public RedisCacheManager cacheManager() {
	RedisCacheManager rcm = RedisCacheManager.create(redisConnectionFactory());
	rcm.setTransactionAware(true);
   	return rcm;
   }  
} 
Find the XML configuration for Redis using <cache:annotation-driven/>.
redis-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:p="http://www.springframework.org/schema/p"
  xmlns:c="http://www.springframework.org/schema/c"
  xmlns:context="http://www.springframework.org/schema/context"  
  xmlns:cache="http://www.springframework.org/schema/cache"  
  xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context.xsd  
        http://www.springframework.org/schema/cache
        http://www.springframework.org/schema/cache/spring-cache.xsd">

    <cache:annotation-driven/>
    <context:property-placeholder location="classpath:redis.properties" ignore-unresolvable="true"/>
       
    <bean id="redisConnectionFactory" 
       class="org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory"
       p:host-name="${redis.host}" p:port="${redis.port}" p:password="${redis.password}" />
    
    <bean id="cacheManager" class="org.springframework.data.redis.cache.RedisCacheManager" 
       factory-method="create" c:connection-factory-ref="redisConnectionFactory" 
       p:transaction-aware="true"/>
</beans> 

Using @Cacheable

@Cacheable indicates that the result of invoking method can be cached and once result is cached, next call to method execution is skipped and only cached result is served. Find some of its elements.
cacheNames: Name of the caches in which method result are stored.
value: Alias for cacheNames.
condition: Spring SpEL expression to make conditional caching.
key: SpEL to compute key dynamically.
keyGenerator: Bean name for custom KeyGenerator.
unless: SpEL to veto method caching.
sync: It is uses to synchronize method invocation when several threads are attempting to load a value for same key.

To compute key, condition or unless, we can use following meta-data in SpEL.
#result: Reference to the result of method.
#root.method: Reference to the method.
#root.target: Reference to the target object.
#root.caches: Reference to the affected caches.
#root.methodName: Shortcut to the method name.
#root.targetClass: Shortcut to target class.
#root.args[1], #p1 or #a1: They give second argument of method. Changing numerical value, we can get other arguments. We can also access arguments by their name.

Now find the sample code snippet to use @Cacheable annotation.
@Cacheable(value= "articleCache", key= "#articleId")		
public Article getArticleById(long articleId) {
  ------
} 
In the above code, method result will be cached with articleCache cache name using key as passed article id. It means for different article id, result will be cached with different key but with same cache name. Once the method result is cached for a key, then for the same key, method will not execute and the cached result will be served.
Find one more example.
@Cacheable(value= "allArticlesCache", unless= "#result.size() == 0")	
public List<Article> getAllArticles(){
  ------
} 
In the above code, method result will not be cached if size of the result will be 0. If we do not provide key, by default it will be ("") or method parameters are used to compute the key if available.

Using @CachePut

@CachePut triggers a cache put operation. It does not skip method execution and result is cached in associated cache for every execution. @CachePut has elements same like @Cacheable such as cacheNames, value, condition, key, unless, keyGenerator etc. Find the sample code snippet to use @CachePut.
@CachePut(value= "articleCache", key= "#article.articleId")
public Article addArticle(Article article){
   ------
} 
The above method will execute for every call and method result will be added or updated in cache corresponding to key for given cache name.

Using @CacheEvict

@CacheEvict triggers a cache evict operation. It does not skip method execution and evicts cache for every execution. It has elements such as cacheNames, value, condition, key, keyGenerator, allEntries etc. If allEntries= true, all entries inside the caches are removed. Find the code snippet to use @CacheEvict.
@CacheEvict(value= "allArticlesCache", allEntries= true)	
public void deleteArticle(long articleId) {
  ------
} 
The above method will execute every call and all the entries of caches will be removed.

Using @Caching

@Caching is the group annotation for multiple cache annotations. It has cacheable, put and evict elements.
Find the code snippet to use @CachePut and @CacheEvict in group using @Caching.
@Caching(
   put= { @CachePut(value= "articleCache", key= "#article.articleId") },
   evict= { @CacheEvict(value= "allArticlesCache", allEntries= true) }
)
public Article updateArticle(Article article) {
   ------
} 
Find the code snippet to use multiple @CacheEvict in group using @Caching.
	
@Caching(
   evict= { 
	@CacheEvict(value= "articleCache", key= "#articleId"),
	@CacheEvict(value= "allArticlesCache", allEntries= true)
   }
)
public void deleteArticle(long articleId) {
   ------
} 

Spring Data Cache + Redis + MySQL CRUD Example

Here we will provide a complete example for Spring Data Cache + Redis + MySQL CRUD operation using JavaConfig. The complete code using JavaConfig as well as XML configuration can be downloaded from link given at the end of the article. Find the project structure in Eclipse for JavaConfig.
Spring Data Redis Cache
Now find the complete code.
RedisConfig.java
package com.concretepage.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisPassword;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;

@Configuration
@EnableCaching
@PropertySource("classpath:redis.properties")
public class RedisConfig {
   @Autowired
   private Environment env;	
	
   @Bean
   public LettuceConnectionFactory redisConnectionFactory() {
	RedisStandaloneConfiguration redisConf = new RedisStandaloneConfiguration();
	redisConf.setHostName(env.getProperty("redis.host"));
	redisConf.setPort(Integer.parseInt(env.getProperty("redis.port")));
	redisConf.setPassword(RedisPassword.of(env.getProperty("redis.password")));	    
        return new LettuceConnectionFactory(redisConf);
   }
   @Bean
   public RedisCacheManager cacheManager() {
	RedisCacheManager rcm = RedisCacheManager.create(redisConnectionFactory());
	rcm.setTransactionAware(true);
   	return rcm;
   }  
} 
ArticleService.java
package com.concretepage.service;
import java.util.ArrayList;
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.cache.annotation.Caching;
import org.springframework.stereotype.Service;
import com.concretepage.entity.Article;
import com.concretepage.repository.ArticleRepository;

@Service
public class ArticleService implements IArticleService {
	@Autowired
	private ArticleRepository articleRepository;
	
	@Override	
	@Cacheable(value= "articleCache", key= "#articleId")		
	public Article getArticleById(long articleId) {
		System.out.println("--- Inside getArticleById() ---");		
		return  articleRepository.findById(articleId).get();
	}
	@Override	
	@Cacheable(value= "allArticlesCache", unless= "#result.size() == 0")	
	public List<Article> getAllArticles(){
		System.out.println("--- Inside getAllArticles() ---");
		List<Article> list = new ArrayList<>();
		articleRepository.findAll().forEach(e -> list.add(e));
		return list;
	}
	@Override	
	@Caching(
		put= { @CachePut(value= "articleCache", key= "#article.articleId") },
		evict= { @CacheEvict(value= "allArticlesCache", allEntries= true) }
	)
	public Article addArticle(Article article){
		System.out.println("--- Inside addArticle() ---");		
		return articleRepository.save(article);
	}
	@Override	
	@Caching(
		put= { @CachePut(value= "articleCache", key= "#article.articleId") },
		evict= { @CacheEvict(value= "allArticlesCache", allEntries= true) }
	)
	public Article updateArticle(Article article) {
		System.out.println("--- Inside updateArticle() ---");		
		return articleRepository.save(article);
	}
	@Override	
	@Caching(
		evict= { 
			@CacheEvict(value= "articleCache", key= "#articleId"),
			@CacheEvict(value= "allArticlesCache", allEntries= true)
		}
	)
	public void deleteArticle(long articleId) {
		System.out.println("--- Inside deleteArticle() ---");		
		articleRepository.delete(articleRepository.findById(articleId).get());
	}
} 
IArticleService.java
package com.concretepage.service;
import java.util.List;
import com.concretepage.entity.Article;

public interface IArticleService {
     List<Article> getAllArticles();
     Article getArticleById(long articleId);
     Article addArticle(Article article);
     Article updateArticle(Article article);
     void deleteArticle(long articleId);
} 
JPAConfig.java
package com.concretepage.config;
import java.util.Properties;
import javax.sql.DataSource;
import org.apache.commons.dbcp2.BasicDataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@Configuration 
@EnableTransactionManagement
@EnableJpaRepositories("com.concretepage.repository")
@PropertySource("classpath:database.properties")
public class JPAConfig {
	@Autowired
        private Environment env;	
	
	@Bean(name="entityManagerFactory")
	public LocalContainerEntityManagerFactoryBean getEntityManagerFactoryBean() {
	    LocalContainerEntityManagerFactoryBean lcemfb = new LocalContainerEntityManagerFactoryBean();
	    lcemfb.setJpaVendorAdapter(getJpaVendorAdapter());
	    lcemfb.setDataSource(getDataSource());
	    lcemfb.setPersistenceUnitName("myJpaPersistenceUnit");
	    lcemfb.setPackagesToScan("com.concretepage.entity");
	    lcemfb.setJpaProperties(jpaProperties());
	    return lcemfb;
	}
	@Bean
	public JpaVendorAdapter getJpaVendorAdapter() {
	    JpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
	    return adapter;
	}
        @Bean
	public DataSource getDataSource() {
	    BasicDataSource dataSource = new BasicDataSource();
	    dataSource.setDriverClassName(env.getProperty("database.driverClassName"));
	    dataSource.setUrl(env.getProperty("database.url"));
	    dataSource.setUsername(env.getProperty("database.username"));
	    dataSource.setPassword(env.getProperty("database.password"));
	    return dataSource;
	}
	@Bean(name="transactionManager")
	public PlatformTransactionManager txManager(){
	    JpaTransactionManager jpaTransactionManager = new JpaTransactionManager(
		getEntityManagerFactoryBean().getObject());
	    return jpaTransactionManager;
	}
        private Properties jpaProperties() {
            Properties properties = new Properties();
            properties.put("hibernate.dialect", env.getProperty("hibernate.dialect"));
            properties.put("hibernate.show_sql", env.getProperty("hibernate.show_sql"));
            properties.put("hibernate.format_sql", env.getProperty("hibernate.format_sql"));
            properties.put("hibernate.id.new_generator_mappings", env.getProperty("hibernate.id.new_generator_mappings"));
            return properties;        
        }	
} 
AppConfig.java
package com.concretepage.config;  
import java.util.List;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration 
@ComponentScan("com.concretepage") 
@EnableWebMvc   
public class AppConfig implements WebMvcConfigurer {  
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
        builder.indentOutput(true);
        converters.add(new MappingJackson2HttpMessageConverter(builder.build()));
    }
} 
WebAppInitializer.java
package com.concretepage.config;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
  
public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer  {
    @Override
    protected Class<?>[] getRootConfigClasses() {
	return new Class[] { AppConfig.class };
    }
    @Override
    protected Class<?>[] getServletConfigClasses() {
        return null;
    }
    @Override
    protected String[] getServletMappings() {
        return new String[]{ "/" };
    } 
} 
Table: articles
CREATE TABLE IF NOT EXISTS `articles` (
  `article_id` int(5) NOT NULL AUTO_INCREMENT,
  `title` varchar(200) NOT NULL,
  `category` varchar(100) NOT NULL,
  PRIMARY KEY (`article_id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1;

INSERT INTO `articles` (`article_id`, `title`, `category`) VALUES
	(1, 'Spring REST Security', 'Spring'),
	(2, 'Java Concurrency', 'Java'); 
Article.java
package com.concretepage.entity;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name="articles")
public class Article implements Serializable { 
	private static final long serialVersionUID = 1L;
	@Id
	@GeneratedValue(strategy=GenerationType.AUTO)
	@Column(name="article_id")
        private long articleId;  
	@Column(name="title")
        private String title;
	@Column(name="category")	
	private String category;
	public long getArticleId() {
		return articleId;
	}
	public void setArticleId(long articleId) {
		this.articleId = articleId;
	}
	public String getTitle() {
		return title;
	}
	public void setTitle(String title) {
		this.title = title;
	}
	public String getCategory() {
		return category;
	}
	public void setCategory(String category) {
		this.category = category;
	}
} 
ArticleRepository.java
package com.concretepage.repository;
import org.springframework.data.repository.CrudRepository;
import com.concretepage.entity.Article;

public interface ArticleRepository extends CrudRepository<Article, Long>  {
} 
database.properties
database.driverClassName=com.mysql.cj.jdbc.Driver
database.url=jdbc:mysql://localhost:3306/concretepage
database.username=root
database.password=cp

hibernate.dialect = org.hibernate.dialect.MySQLDialect
hibernate.show_sql = true 
hibernate.format_sql = true
hibernate.id.new_generator_mappings = false 
redis.properties
redis.host=localhost
redis.port=6379
redis.password= 
ArticleController.java
package com.concretepage.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
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.util.UriComponentsBuilder;
import com.concretepage.entity.Article;
import com.concretepage.service.IArticleService;

@Controller
@RequestMapping("user")
public class ArticleController {
	@Autowired
	private IArticleService articleService;
	
	@GetMapping("article/{id}")
	public ResponseEntity<Article> getArticleById(@PathVariable("id") Long id) {
		Article article = articleService.getArticleById(id);
		return new ResponseEntity<Article>(article, HttpStatus.OK);
	}
	@GetMapping("articles")
	public ResponseEntity<List<Article>> getAllArticles() {
		List<Article> list = articleService.getAllArticles();
		return new ResponseEntity<List<Article>>(list, HttpStatus.OK);
	}
	@PostMapping("article")
	public ResponseEntity<Void> addArticle(@RequestBody Article article, UriComponentsBuilder builder) {
		Article savedArticle = articleService.addArticle(article);  
                HttpHeaders headers = new HttpHeaders();
                headers.setLocation(builder.path("/article/{id}").buildAndExpand(savedArticle.getArticleId()).toUri());
                return new ResponseEntity<Void>(headers, HttpStatus.CREATED);
	}
	@PutMapping("article")
	public ResponseEntity<Article> updateArticle(@RequestBody Article article) {
		articleService.updateArticle(article);
		return new ResponseEntity<Article>(article, HttpStatus.OK);
	}
	@DeleteMapping("article/{id}")
	public ResponseEntity<Void> deleteArticle(@PathVariable("id") Long id) {
		articleService.deleteArticle(id);
		return new ResponseEntity<Void>(HttpStatus.NO_CONTENT);
	}	
} 
RestClientUtil.java
package com.concretepage.client;
import java.net.URI;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;
import com.concretepage.entity.Article;

public class RestClientUtil {
    public void getArticleByIdDemo(long id) {
    	HttpHeaders headers = new HttpHeaders();
    	headers.setContentType(MediaType.APPLICATION_JSON);
        RestTemplate restTemplate = new RestTemplate();
	String url = "http://localhost:8080/spring-app/user/article/{id}";
        HttpEntity<String> requestEntity = new HttpEntity<String>(headers);
        ResponseEntity<Article> responseEntity = restTemplate.exchange(url, HttpMethod.GET, requestEntity, Article.class, id);
        Article article = responseEntity.getBody();
        System.out.println("Id:"+article.getArticleId()+", Title:"+article.getTitle()
                 +", Category:"+article.getCategory());      
    }
    public void getAllArticlesDemo() {
	HttpHeaders headers = new HttpHeaders();
	headers.setContentType(MediaType.APPLICATION_JSON);
        RestTemplate restTemplate = new RestTemplate();
	String url = "http://localhost:8080/spring-app/user/articles";
        HttpEntity<String> requestEntity = new HttpEntity<String>(headers);
        ResponseEntity<Article[]> responseEntity = restTemplate.exchange(url, HttpMethod.GET, requestEntity, Article[].class);
        Article[] articles = responseEntity.getBody();
        for(Article article : articles) {
              System.out.println("Id:"+article.getArticleId()+", Title:"+article.getTitle()
                      +", Category: "+article.getCategory());
        }
    }
    public void addArticleDemo(Article objArticle) {
    	HttpHeaders headers = new HttpHeaders();
    	headers.setContentType(MediaType.APPLICATION_JSON);
        RestTemplate restTemplate = new RestTemplate();
	String url = "http://localhost:8080/spring-app/user/article";
        HttpEntity<Article> requestEntity = new HttpEntity<Article>(objArticle, headers);
        URI uri = restTemplate.postForLocation(url, requestEntity);
        System.out.println(uri.getPath());    	
    }
    public void updateArticleDemo(Article objArticle) {
    	HttpHeaders headers = new HttpHeaders();
    	headers.setContentType(MediaType.APPLICATION_JSON);
        RestTemplate restTemplate = new RestTemplate();
	String url = "http://localhost:8080/spring-app/user/article";
        HttpEntity<Article> requestEntity = new HttpEntity<Article>(objArticle, headers);
        restTemplate.put(url, requestEntity);
    }
    public void deleteArticleDemo(long id) {
    	HttpHeaders headers = new HttpHeaders();
    	headers.setContentType(MediaType.APPLICATION_JSON);
        RestTemplate restTemplate = new RestTemplate();
	String url = "http://localhost:8080/spring-app/user/article/{id}";
        HttpEntity<Article> requestEntity = new HttpEntity<Article>(headers);
        restTemplate.exchange(url, HttpMethod.DELETE, requestEntity, Void.class, id);        
    }
    public static void main(String args[]) {
    	RestClientUtil util = new RestClientUtil();
    	//Add article
	Article objArticle = new Article();
	objArticle.setTitle("Spring REST Security");
	objArticle.setCategory("Spring");
     	//util.addArticleDemo(objArticle);
        
    	//Update article
	objArticle.setArticleId(1);
	objArticle.setTitle("Java Concurrency");
	objArticle.setCategory("Java");	    
    	//util.updateArticleDemo(objArticle);
	    
    	//util.deleteArticleDemo(2);
        util.getArticleByIdDemo(1);	    
        System.out.println("---- All articles ----");
    	util.getAllArticlesDemo();    	
    }    
} 

Run Application

To test our demo application, find the steps.
1. Install and start Redis using the link.
2. If you are using Windows OS, you can install Cygwin first and then install Redis in it.
3. Redis will start on localhost at port 6379.
4. Import table in MySQL database given above in the article.
5. Download source code from the download link given at the end of the article.
6. Go to the root directory of the project using command prompt.
7. Build the project using Maven with following command.
mvn clean package 
We will find WAR file in the target directory. If we want to build the project using Gradle, use following command.
gradle clean build 
We will find WAR file in the build\libs directory.
8. Deploy the WAR file in tomcat.
9. To test the application, use RestClientUtil.java file given in the demo project.

References

Spring Data Redis
@Cacheable
@CachePut
@CacheEvict
@Caching

Download Source Code

POSTED BY
ARVIND RAI
ARVIND RAI
FIND MORE TUTORILAS


©2018 concretepage.com | Privacy Policy | Contact Us