Spring Boot— CRUD example with Caching

@Caching(evict = {
@CacheEvict(value=”delivery”, allEntries=true),
@CacheEvict(value=”deliveries”, allEntries=true) })

package com.buddhi.service;

import com.buddhi.dto.DeliveryDto;
import com.buddhi.model.Delivery;
import com.buddhi.repository.DeliveryRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

import static org.springframework.beans.BeanUtils.copyProperties;

@Slf4j
@Service
public class DeliveryService {
@Autowired
DeliveryRepository deliveryRepository;

@Cacheable("deliveries")
public List<DeliveryDto> findAll(){
log.info("DeliveryService: findAll");
List<Delivery> deliveries = deliveryRepository.findAll();
List<DeliveryDto> deliveryDtos = new ArrayList<>();
for (Delivery delivery:deliveries){
DeliveryDto deliveryDto = new DeliveryDto();
copyProperties(delivery, deliveryDto);
deliveryDtos.add(deliveryDto);
}
return deliveryDtos;
}

@Cacheable("delivery")
public DeliveryDto findById(Long id){
log.info("DeliveryService: findById");
Delivery delivery = deliveryRepository.findById(id).orElse(null);
DeliveryDto deliveryDto = new DeliveryDto();
copyProperties(delivery,deliveryDto);
return deliveryDto;
}

@Caching(evict = {
@CacheEvict(value="delivery", allEntries=true),
@CacheEvict(value="deliveries", allEntries=true)})
public Delivery saveOrUpdate(DeliveryDto deliveryDto){
log.info("DeliveryService: saveOrUpdate, {}", deliveryDto.getPickupName());
Delivery delivery = Delivery.builder()
.pickupName(deliveryDto.getPickupName())
...
.build();
if(deliveryDto.getId()!=null){
delivery.setId(deliveryDto.getId());
}
return deliveryRepository.save(delivery);
}
}
package com.buddhi.repository;

import com.buddhi.model.Delivery;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface DeliveryRepository extends JpaRepository<Delivery, Long> {
}
package com.buddhi.controller;

import com.buddhi.dto.DeliveryDto;
import com.buddhi.model.Delivery;
import com.buddhi.service.DeliveryService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@Slf4j
@RestController
@RequestMapping(path = "/deliveries")
public class DeliveryController {
@Autowired
DeliveryService deliveryService;

@PostMapping(path = "")
public ResponseEntity<Long> createOrUpdateDelivery(@RequestBody DeliveryDto deliveryDto) {
log.info("DeliveryController: createOrUpdateDelivery");
Delivery delivery = deliveryService.saveOrUpdate(deliveryDto);
return new ResponseEntity<>(delivery.getId(), HttpStatus.OK);
}

@GetMapping(path = "")
public ResponseEntity<List<DeliveryDto>> getDeliveries() {
log.info("DeliveryController: getDeliveries");
List<DeliveryDto> deliveries = deliveryService.findAll();
return new ResponseEntity<>(deliveries, HttpStatus.OK);
}

@GetMapping(path = "/{id}")
public ResponseEntity<DeliveryDto> getDelivery(@PathVariable Long id) {
log.info("DeliveryController: getDelivery");
DeliveryDto delivery = deliveryService.findById(id);
return new ResponseEntity<>(delivery, HttpStatus.OK);
}
}
package com.buddhi.dto;

import com.buddhi.util.CustomDateAndTimeDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import lombok.Data;

import java.util.Date;

@Data
public class DeliveryDto {
private Long id;
//pickup
private String pickupName;
private String pickupAddress;
@JsonDeserialize(using= CustomDateAndTimeDeserialize.class)
private Date pickupDateTime;
private String[] pickupContactNumbers;
private String pickupComment;
//drop
private String dropName;
private String dropAddress;
private String[] dropContactNumbers;
private String dropComment;
}
package com.buddhi.model;

import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.*;
import java.util.Date;

@AllArgsConstructor
@NoArgsConstructor
@Data
@Entity
@Builder
@Table(name = "delivery")
public class Delivery {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "pickup_name")
private String pickupName;
@Column(name = "pickup_address")
private String pickupAddress;
@Column(name = "pickup_datetime")
@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss")
private Date pickupDateTime;
@Column(name = "pickup_contact_numbers")
private String pickupContactNumbers;
@Column(name = "pickup_comment")
private String pickupComment;
@Column(name = "drop_name")
private String dropName;
@Column(name = "drop_address")
private String dropAddress;
@Column(name = "drop_contact_numbers")
private String dropContactNumbers;
@Column(name = "drop_comment")
private String dropComment;
}
package com.buddhi;

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

@SpringBootApplication
@EnableCaching
public class DeliveryApplication {

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

}
server:
port: 8080

spring:
h2:
console:
enabled: true
datasource
:
driver-class-name: org.h2.Driver
url: jdbc:h2:mem:testdb
username: sa
password: password
platform: org.hibernate.dialect.Oracle10gDialect
continue-on-error: true
jpa
:
hibernate:
ddl-auto: none

logging:
level:
root: info
pattern:
console: "%d{yyyy-MM-dd HH:mm:ss.SSS} %5p --- [%15.15t] %-40.40logger{39} :: %X{correlationId} : %m%n%ex{full}"
<?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>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.3.RELEASE</version>
<relativePath/> <!-- lookup parent from delivery -->
</parent>
<groupId>com</groupId>
<artifactId>buddhi</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>buddhi</name>
<description>Demo project for Spring Boot</description>

<properties>
<java.version>1.8</java.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</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-web</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.9</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

</project>
package com.buddhi.config;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.UUID;

@Component
public class CorrelationInterceptor extends HandlerInterceptorAdapter {
private static final String CORRELATION_ID_HEADER_NAME = "X-Correlation-Id";
private static final String CORRELATION_ID_LOG_VAR_NAME = "correlationId";
@Override
public boolean preHandle(final HttpServletRequest request, final HttpServletResponse response,
final Object handler) throws Exception {
final String correlationId = getCorrelationIdFromHeader(request);
MDC.put(CORRELATION_ID_LOG_VAR_NAME, correlationId);
return true;
}
@Override
public void afterCompletion(final HttpServletRequest request, final HttpServletResponse response,
final Object handler, final Exception ex) throws Exception {
MDC.remove(CORRELATION_ID_LOG_VAR_NAME);
}
private String getCorrelationIdFromHeader(final HttpServletRequest request) {
String correlationId = request.getHeader(CORRELATION_ID_HEADER_NAME);
if (StringUtils.isBlank(correlationId)) {
correlationId = generateUniqueCorrelationId();
}
return correlationId;
}
private String generateUniqueCorrelationId() {
return UUID.randomUUID().toString();
}
}
package com.buddhi.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Autowired
private CorrelationInterceptor correlationInterceptor;

@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(correlationInterceptor);
}
}
https://start.spring.io/
package com.buddhi.util;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.io.Serializable;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

@Slf4j
public class CustomDateAndTimeDeserialize extends JsonDeserializer<Date> {
private SimpleDateFormat dateFormat = new SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss");

@Override
public Date deserialize(JsonParser paramJsonParser,
DeserializationContext paramDeserializationContext)
throws IOException, JsonProcessingException {
String str = paramJsonParser.getText().trim();
try {
return dateFormat.parse(str);
} catch (ParseException e) {
log.error(e.getMessage());
e.printStackTrace();
}
return paramDeserializationContext.parseDate(str);
}
}
create table if not exists delivery
(
id int auto_increment,
pickup_name varchar(100) null,
pickup_address varchar(200) null,
pickup_datetime datetime null,
pickup_contact_numbers varchar(100) null,
pickup_comment text null,
drop_name varchar(100) null,
drop_address varchar(200) null,
drop_contact_numbers varchar(45) null,
drop_comment text null,
primary key (id)
);

create index idx_pdt on delivery (pickup_datetime);

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store