BAEL-5696: Refactors spring caching TTL tutorial (#12825)
Co-authored-by: Abdul <abdul.wahab@monese.com>
This commit is contained in:
parent
da67c2d032
commit
4a740338e4
3
pom.xml
3
pom.xml
@ -560,8 +560,7 @@
|
|||||||
<module>spring-boot-rest</module>
|
<module>spring-boot-rest</module>
|
||||||
|
|
||||||
<module>spring-caching</module>
|
<module>spring-caching</module>
|
||||||
<module>spring-caching-2/redis</module>
|
<module>spring-caching-2</module>
|
||||||
<module>spring-caching-2/ttl</module>
|
|
||||||
|
|
||||||
<module>spring-cloud-modules</module>
|
<module>spring-cloud-modules</module>
|
||||||
<!-- <module>spring-cloud-cli</module> --> <!-- Not a maven project -->
|
<!-- <module>spring-cloud-cli</module> --> <!-- Not a maven project -->
|
||||||
|
@ -6,13 +6,13 @@
|
|||||||
<artifactId>spring-caching-2</artifactId>
|
<artifactId>spring-caching-2</artifactId>
|
||||||
<version>0.1-SNAPSHOT</version>
|
<version>0.1-SNAPSHOT</version>
|
||||||
<name>spring-caching-2</name>
|
<name>spring-caching-2</name>
|
||||||
<packaging>war</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>com.baeldung</groupId>
|
<groupId>com.baeldung</groupId>
|
||||||
<artifactId>parent-boot-2</artifactId>
|
<artifactId>parent-boot-2</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
<relativePath>../../parent-boot-2/pom.xml</relativePath>
|
<relativePath>../parent-boot-2/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
@ -63,4 +63,16 @@
|
|||||||
<embedded.redis.version>0.7.3</embedded.redis.version>
|
<embedded.redis.version>0.7.3</embedded.redis.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<resources>
|
||||||
|
<resource>
|
||||||
|
<directory>resources</directory>
|
||||||
|
<targetPath>${project.build.outputDirectory}</targetPath>
|
||||||
|
<includes>
|
||||||
|
<include>application.properties</include>
|
||||||
|
</includes>
|
||||||
|
</resource>
|
||||||
|
</resources>
|
||||||
|
</build>
|
||||||
|
|
||||||
</project>
|
</project>
|
@ -1,3 +0,0 @@
|
|||||||
### Relevant articles:
|
|
||||||
|
|
||||||
- [Spring Boot Cache with Redis](https://www.baeldung.com/spring-boot-redis-cache)
|
|
@ -1 +0,0 @@
|
|||||||
INSERT INTO ITEM VALUES('abc','ITEM1');
|
|
@ -1,5 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<configuration>
|
|
||||||
<include resource="org/springframework/boot/logging/logback/base.xml" />
|
|
||||||
<logger name="org.springframework" level="OFF"/>
|
|
||||||
</configuration>
|
|
@ -6,8 +6,8 @@ import org.springframework.scheduling.annotation.EnableScheduling;
|
|||||||
|
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
@EnableScheduling
|
@EnableScheduling
|
||||||
public class Application {
|
public class CachingTTLApplication {
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
SpringApplication.run(Application.class, args);
|
SpringApplication.run(CachingTTLApplication.class, args);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -5,7 +5,6 @@ import com.baeldung.caching.ttl.model.Hotel;
|
|||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.PathVariable;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
@ -27,10 +26,4 @@ public class HotelController {
|
|||||||
public List<Hotel> getAllHotels() {
|
public List<Hotel> getAllHotels() {
|
||||||
return hotelService.getAllHotels();
|
return hotelService.getAllHotels();
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping(value = "/{id}")
|
|
||||||
@ResponseStatus(HttpStatus.OK)
|
|
||||||
public Hotel getHotelById(@PathVariable Long id) {
|
|
||||||
return hotelService.getHotelById(id);
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -17,7 +17,7 @@ public class City implements Serializable {
|
|||||||
|
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
private double cityCentreLatitude;
|
private double cityCentreLattitude;
|
||||||
private double cityCentreLongitude;
|
private double cityCentreLongitude;
|
||||||
|
|
||||||
public City() {}
|
public City() {}
|
||||||
@ -25,7 +25,7 @@ public class City implements Serializable {
|
|||||||
public City(Long id, String name, double cityCentreLatitude, double cityCentreLongitude) {
|
public City(Long id, String name, double cityCentreLatitude, double cityCentreLongitude) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.cityCentreLatitude = cityCentreLatitude;
|
this.cityCentreLattitude = cityCentreLatitude;
|
||||||
this.cityCentreLongitude = cityCentreLongitude;
|
this.cityCentreLongitude = cityCentreLongitude;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,7 +42,7 @@ public class City implements Serializable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public double getCityCentreLatitude() {
|
public double getCityCentreLatitude() {
|
||||||
return cityCentreLatitude;
|
return cityCentreLattitude;
|
||||||
}
|
}
|
||||||
|
|
||||||
public double getCityCentreLongitude() {
|
public double getCityCentreLongitude() {
|
||||||
@ -56,7 +56,7 @@ public class City implements Serializable {
|
|||||||
|
|
||||||
City city = (City) o;
|
City city = (City) o;
|
||||||
|
|
||||||
if (Double.compare(city.cityCentreLatitude, cityCentreLatitude) != 0) return false;
|
if (Double.compare(city.cityCentreLattitude, cityCentreLattitude) != 0) return false;
|
||||||
if (Double.compare(city.cityCentreLongitude, cityCentreLongitude) != 0) return false;
|
if (Double.compare(city.cityCentreLongitude, cityCentreLongitude) != 0) return false;
|
||||||
if (!Objects.equals(id, city.id)) return false;
|
if (!Objects.equals(id, city.id)) return false;
|
||||||
return Objects.equals(name, city.name);
|
return Objects.equals(name, city.name);
|
@ -22,7 +22,7 @@ public class Hotel implements Serializable {
|
|||||||
private City city;
|
private City city;
|
||||||
|
|
||||||
private String address;
|
private String address;
|
||||||
private double latitude;
|
private double lattitude;
|
||||||
private double longitude;
|
private double longitude;
|
||||||
private boolean deleted = false;
|
private boolean deleted = false;
|
||||||
|
|
||||||
@ -34,7 +34,7 @@ public class Hotel implements Serializable {
|
|||||||
Double rating,
|
Double rating,
|
||||||
City city,
|
City city,
|
||||||
String address,
|
String address,
|
||||||
double latitude,
|
double lattitude,
|
||||||
double longitude,
|
double longitude,
|
||||||
boolean deleted) {
|
boolean deleted) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
@ -42,7 +42,7 @@ public class Hotel implements Serializable {
|
|||||||
this.rating = rating;
|
this.rating = rating;
|
||||||
this.city = city;
|
this.city = city;
|
||||||
this.address = address;
|
this.address = address;
|
||||||
this.latitude = latitude;
|
this.lattitude = lattitude;
|
||||||
this.longitude = longitude;
|
this.longitude = longitude;
|
||||||
this.deleted = deleted;
|
this.deleted = deleted;
|
||||||
}
|
}
|
||||||
@ -88,11 +88,11 @@ public class Hotel implements Serializable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public double getLatitude() {
|
public double getLatitude() {
|
||||||
return latitude;
|
return lattitude;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setLatitude(double latitude) {
|
public void setLatitude(double latitude) {
|
||||||
this.latitude = latitude;
|
this.lattitude = latitude;
|
||||||
}
|
}
|
||||||
|
|
||||||
public double getLongitude() {
|
public double getLongitude() {
|
||||||
@ -118,7 +118,7 @@ public class Hotel implements Serializable {
|
|||||||
|
|
||||||
Hotel hotel = (Hotel) o;
|
Hotel hotel = (Hotel) o;
|
||||||
|
|
||||||
if (Double.compare(hotel.latitude, latitude) != 0) return false;
|
if (Double.compare(hotel.lattitude, lattitude) != 0) return false;
|
||||||
if (Double.compare(hotel.longitude, longitude) != 0) return false;
|
if (Double.compare(hotel.longitude, longitude) != 0) return false;
|
||||||
if (deleted != hotel.deleted) return false;
|
if (deleted != hotel.deleted) return false;
|
||||||
if (!Objects.equals(id, hotel.id)) return false;
|
if (!Objects.equals(id, hotel.id)) return false;
|
@ -16,13 +16,4 @@ public interface HotelRepository extends JpaRepository<Hotel, Long> {
|
|||||||
}
|
}
|
||||||
return findAll();
|
return findAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
default Optional<Hotel> getHotelById(Long hotelId) {
|
|
||||||
try {
|
|
||||||
Thread.sleep(500);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
return findById(hotelId);
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
package com.baeldung.caching.ttl.service;
|
||||||
|
|
||||||
|
import com.baeldung.caching.ttl.repository.HotelRepository;
|
||||||
|
import com.baeldung.caching.ttl.model.Hotel;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.cache.annotation.CacheEvict;
|
||||||
|
import org.springframework.cache.annotation.Cacheable;
|
||||||
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class HotelService {
|
||||||
|
|
||||||
|
private final HotelRepository hotelRepository;
|
||||||
|
Logger logger = LoggerFactory.getLogger(HotelService.class);
|
||||||
|
|
||||||
|
HotelService(HotelRepository hotelRepository) {
|
||||||
|
this.hotelRepository = hotelRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Cacheable("hotels")
|
||||||
|
public List<Hotel> getAllHotels() {
|
||||||
|
return hotelRepository.getAllHotels();
|
||||||
|
}
|
||||||
|
|
||||||
|
@CacheEvict(value = "hotels", allEntries = true)
|
||||||
|
@Scheduled(fixedRateString = "43200")
|
||||||
|
public void emptyHotelsCache() {
|
||||||
|
logger.info("emptying Hotels cache");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -2,11 +2,12 @@ spring.datasource.url=jdbc:h2:mem:testdb;DB_CLOSE_ON_EXIT=FALSE
|
|||||||
spring.datasource.driverClassName=org.h2.Driver
|
spring.datasource.driverClassName=org.h2.Driver
|
||||||
spring.datasource.username=sa
|
spring.datasource.username=sa
|
||||||
spring.datasource.password=
|
spring.datasource.password=
|
||||||
|
|
||||||
# Enabling H2 Console
|
# Enabling H2 Console
|
||||||
spring.h2.console.enabled=true
|
spring.h2.console.enabled=true
|
||||||
spring.h2.console.path=/h2
|
spring.h2.console.path=/h2
|
||||||
|
spring.jpa.hibernate.ddl-auto=update
|
||||||
|
#setting cache TTL
|
||||||
|
caching.spring.hotelListTTL=43200
|
||||||
# Connection details
|
# Connection details
|
||||||
#spring.redis.host=localhost
|
#spring.redis.host=localhost
|
||||||
#spring.redis.port=6379
|
#spring.redis.port=6379
|
67
spring-caching-2/src/main/resources/data.sql
Normal file
67
spring-caching-2/src/main/resources/data.sql
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
DROP TABLE IF EXISTS ITEM;
|
||||||
|
create table ITEM
|
||||||
|
(
|
||||||
|
ID CHARACTER VARYING not null,
|
||||||
|
DESCRIPTION CHARACTER VARYING,
|
||||||
|
constraint ITEM_PK
|
||||||
|
primary key (ID)
|
||||||
|
);
|
||||||
|
DROP TABLE IF EXISTS HOTEL;
|
||||||
|
DROP TABLE IF EXISTS CITY;
|
||||||
|
create table CITY
|
||||||
|
(
|
||||||
|
id bigint,
|
||||||
|
name varchar,
|
||||||
|
city_centre_lattitude double,
|
||||||
|
city_centre_longitude double,
|
||||||
|
constraint city_pk
|
||||||
|
primary key (id)
|
||||||
|
);
|
||||||
|
create table hotel
|
||||||
|
(
|
||||||
|
id bigint auto_increment,
|
||||||
|
name varchar,
|
||||||
|
deleted boolean,
|
||||||
|
rating double,
|
||||||
|
city_id bigint,
|
||||||
|
address varchar,
|
||||||
|
lattitude varchar,
|
||||||
|
longitude varchar,
|
||||||
|
constraint hotel_pk
|
||||||
|
primary key (id),
|
||||||
|
constraint "hotel_CITY_null_fk"
|
||||||
|
foreign key (city_id) references CITY (id)
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
INSERT INTO ITEM VALUES('abc','ITEM1');
|
||||||
|
|
||||||
|
|
||||||
|
INSERT INTO city(id, name, city_centre_lattitude, city_centre_longitude)
|
||||||
|
VALUES (1, 'Amsterdam', 52.368780, 4.903303);
|
||||||
|
INSERT INTO city(id, name, city_centre_lattitude, city_centre_longitude)
|
||||||
|
VALUES (2, 'Manchester', 53.481062, -2.237706);
|
||||||
|
|
||||||
|
|
||||||
|
INSERT INTO hotel(name, deleted, rating, city_id, address, lattitude, longitude)
|
||||||
|
VALUES ('Monaghan Hotel', false, 9.2, 1, 'Weesperbuurt en Plantage', 52.364799, 4.908971);
|
||||||
|
INSERT INTO hotel(name, deleted, rating, city_id, address, lattitude, longitude)
|
||||||
|
VALUES ('The Thornton Council Hotel', false, 6.3, 1, 'Waterlooplein', 52.3681563, 4.9010029);
|
||||||
|
INSERT INTO hotel(name, deleted, rating, city_id, address, lattitude, longitude)
|
||||||
|
VALUES ('McZoe Trescothiks Hotel', false, 9.8, 1, 'Oude Stad, Harlem', 52.379577, 4.633547);
|
||||||
|
INSERT INTO hotel(name, deleted, rating, city_id, address, lattitude, longitude)
|
||||||
|
VALUES ('Stay Schmtay Hotel', false, 8.7, 1, 'Jan van Galenstraat', 52.3756755, 4.8668628);
|
||||||
|
INSERT INTO hotel(name, deleted, rating, city_id, address, lattitude, longitude)
|
||||||
|
VALUES ('Fitting Image Hotel', false, NULL, 1, 'Staatsliedenbuurt', 52.380936, 4.8708297);
|
||||||
|
INSERT INTO hotel(name, deleted, rating, city_id, address, lattitude, longitude)
|
||||||
|
VALUES ('Raymond of Amsterdam Hotel', false, NULL, 1, '22 High Avenue', 52.3773989, 4.8846443);
|
||||||
|
INSERT INTO hotel(name, deleted, rating, city_id, address, lattitude, longitude)
|
||||||
|
VALUES ('201 Deansgate Hotel', false, 7.3, 2, '201 Deansgate', 53.4788305, -2.2484721);
|
||||||
|
INSERT INTO hotel(name, deleted, rating, city_id, address, lattitude, longitude)
|
||||||
|
VALUES ('Fountain Street Hotel', true, 3.0, 2, '35 Fountain Street', 53.4811298, -2.2402227);
|
||||||
|
INSERT INTO hotel(name, deleted, rating, city_id, address, lattitude, longitude)
|
||||||
|
VALUES ('Sunlight House', false, 4.3, 2, 'Little Quay St', 53.4785129, -2.2505943);
|
||||||
|
INSERT INTO hotel(name, deleted, rating, city_id, address, lattitude, longitude)
|
||||||
|
VALUES ('St Georges House', false, 9.6, 2, '56 Peter St', 53.477822, -2.2462002);
|
||||||
|
INSERT INTO hotel(name, deleted, rating, city_id, address, lattitude, longitude)
|
||||||
|
VALUES ('Marriot Bonvoy', false, 9.6, 1, 'Hans Zimmerstraat', 53.477872, -2.2462003);
|
@ -0,0 +1,43 @@
|
|||||||
|
package com.baeldung.caching.ttl;
|
||||||
|
|
||||||
|
import com.baeldung.caching.ttl.repository.HotelRepository;
|
||||||
|
import org.junit.jupiter.api.DisplayName;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import org.springframework.test.context.jdbc.Sql;
|
||||||
|
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||||
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.hasSize;
|
||||||
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
|
|
||||||
|
@ExtendWith(SpringExtension.class)
|
||||||
|
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
|
||||||
|
@AutoConfigureMockMvc
|
||||||
|
@Sql(executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD, scripts = "classpath:data.sql")
|
||||||
|
@SlowTest
|
||||||
|
class HotelControllerIntegrationTest {
|
||||||
|
@Autowired private MockMvc mockMvc;
|
||||||
|
@Autowired private HotelRepository repository;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("When all hotels requested then request is successful")
|
||||||
|
void whenAllHotelsRequested_thenRequestIsSuccessful() throws Exception {
|
||||||
|
mockMvc
|
||||||
|
.perform(get("/hotel"))
|
||||||
|
.andExpect(status().isOk());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("When all hotels are requested then they correct number of hotels is returned")
|
||||||
|
void whenAllHotelsRequested_thenReturnAllHotels() throws Exception {
|
||||||
|
mockMvc
|
||||||
|
.perform(get("/hotel"))
|
||||||
|
.andExpect(jsonPath("$", hasSize((int) repository.findAll().stream().count())));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,4 @@
|
|||||||
|
package com.baeldung.caching.ttl;
|
||||||
|
|
||||||
|
public @interface SlowTest {
|
||||||
|
}
|
15
spring-caching-2/src/test/resources/application.properties
Normal file
15
spring-caching-2/src/test/resources/application.properties
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
spring.datasource.url=jdbc:h2:mem:testdb;DB_CLOSE_ON_EXIT=FALSE
|
||||||
|
spring.datasource.driverClassName=org.h2.Driver
|
||||||
|
spring.datasource.username=sa
|
||||||
|
spring.datasource.password=
|
||||||
|
|
||||||
|
# Enabling H2 Console
|
||||||
|
spring.h2.console.enabled=true
|
||||||
|
spring.h2.console.path=/h2
|
||||||
|
spring.jpa.hibernate.ddl-auto=update
|
||||||
|
spring.jpa.show-sql=false
|
||||||
|
caching.spring.hotelListTTL=43200
|
||||||
|
# Connection details
|
||||||
|
#spring.redis.host=localhost
|
||||||
|
#spring.redis.port=6379
|
||||||
|
server.port=8000
|
@ -1,49 +0,0 @@
|
|||||||
<?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>
|
|
||||||
<artifactId>spring-caching-3</artifactId>
|
|
||||||
<version>0.1-SNAPSHOT</version>
|
|
||||||
<name>spring-caching-3</name>
|
|
||||||
|
|
||||||
<parent>
|
|
||||||
<groupId>org.springframework.boot</groupId>
|
|
||||||
<artifactId>spring-boot-starter-parent</artifactId>
|
|
||||||
<version>2.3.4.RELEASE</version>
|
|
||||||
</parent>
|
|
||||||
|
|
||||||
<dependencies>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework.data</groupId>
|
|
||||||
<artifactId>spring-data-commons</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<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-validation</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.h2database</groupId>
|
|
||||||
<artifactId>h2</artifactId>
|
|
||||||
<scope>runtime</scope>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework.boot</groupId>
|
|
||||||
<artifactId>spring-boot-starter-test</artifactId>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.google.guava</groupId>
|
|
||||||
<artifactId>guava</artifactId>
|
|
||||||
<version>30.0-jre</version>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
</project>
|
|
@ -1,34 +0,0 @@
|
|||||||
package com.baeldung.caching.ttl.config;
|
|
||||||
|
|
||||||
import com.google.common.cache.Cache;
|
|
||||||
import com.google.common.cache.CacheBuilder;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
public class GuavaCachingConfig<T> {
|
|
||||||
|
|
||||||
private Cache<Long, T> cache;
|
|
||||||
|
|
||||||
Logger logger = LoggerFactory.getLogger(GuavaCachingConfig.class);
|
|
||||||
|
|
||||||
public GuavaCachingConfig(int expiryDuration, TimeUnit timeUnit) {
|
|
||||||
cache = CacheBuilder.newBuilder()
|
|
||||||
.expireAfterWrite(expiryDuration, timeUnit)
|
|
||||||
.concurrencyLevel(Runtime.getRuntime().availableProcessors())
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
public T get(Long key) {
|
|
||||||
return cache.getIfPresent(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void add(Long key, T value) {
|
|
||||||
if(key != null && value != null) {
|
|
||||||
cache.put(key, value);
|
|
||||||
logger.info(
|
|
||||||
String.format("A %s record stored in Cache with key: %s", value.getClass().getSimpleName(), key));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,21 +0,0 @@
|
|||||||
package com.baeldung.caching.ttl.service;
|
|
||||||
|
|
||||||
import com.baeldung.caching.ttl.config.GuavaCachingConfig;
|
|
||||||
import com.baeldung.caching.ttl.model.Hotel;
|
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
|
||||||
import org.springframework.context.annotation.Bean;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
@Component
|
|
||||||
public class GuavaCacheCustomizer {
|
|
||||||
|
|
||||||
@Value("${caching.guava.hotelItemTTL}")
|
|
||||||
Integer hotelItemTTL;
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public GuavaCachingConfig<Hotel> hotelGuavaCacheStore() {
|
|
||||||
return new GuavaCachingConfig<>(hotelItemTTL, TimeUnit.MILLISECONDS);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,52 +0,0 @@
|
|||||||
package com.baeldung.caching.ttl.service;
|
|
||||||
|
|
||||||
import com.baeldung.caching.ttl.repository.HotelRepository;
|
|
||||||
import com.baeldung.caching.ttl.config.GuavaCachingConfig;
|
|
||||||
import com.baeldung.caching.ttl.exception.ElementNotFoundException;
|
|
||||||
import com.baeldung.caching.ttl.model.Hotel;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import org.springframework.cache.annotation.CacheEvict;
|
|
||||||
import org.springframework.cache.annotation.Cacheable;
|
|
||||||
import org.springframework.scheduling.annotation.Scheduled;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@Service
|
|
||||||
public class HotelService {
|
|
||||||
|
|
||||||
private final HotelRepository hotelRepository;
|
|
||||||
private final GuavaCachingConfig<Hotel> hotelGuavaCachingConfig;
|
|
||||||
Logger logger = LoggerFactory.getLogger(HotelService.class);
|
|
||||||
|
|
||||||
HotelService(HotelRepository hotelRepository, GuavaCachingConfig<Hotel> hotelGuavaCachingConfig) {
|
|
||||||
this.hotelRepository = hotelRepository;
|
|
||||||
this.hotelGuavaCachingConfig = hotelGuavaCachingConfig;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Cacheable("hotels")
|
|
||||||
public List<Hotel> getAllHotels() {
|
|
||||||
return hotelRepository.getAllHotels();
|
|
||||||
}
|
|
||||||
|
|
||||||
@CacheEvict(value = "hotels", allEntries = true)
|
|
||||||
@Scheduled(fixedRateString = "${caching.spring.hotelListTTL}")
|
|
||||||
public void emptyHotelsCache() {
|
|
||||||
logger.info("emptying Hotels cache");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public Hotel getHotelById(Long hotelId) {
|
|
||||||
if (hotelGuavaCachingConfig.get(hotelId) != null) {
|
|
||||||
logger.info(String.format("hotel with id: %s found in cache", hotelId));
|
|
||||||
return hotelGuavaCachingConfig.get(hotelId);
|
|
||||||
}
|
|
||||||
logger.info(String.format("hotel with id: %s is being searched in DB", hotelId));
|
|
||||||
Hotel hotel = hotelRepository.getHotelById(hotelId)
|
|
||||||
.orElseThrow(() -> new ElementNotFoundException(String.format("Hotel with id %s not found", hotelId)));
|
|
||||||
hotelGuavaCachingConfig.add(hotelId, hotel);
|
|
||||||
return hotel;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,15 +0,0 @@
|
|||||||
spring:
|
|
||||||
jpa:
|
|
||||||
open-in-view: true
|
|
||||||
hibernate:
|
|
||||||
ddl-auto: create-drop
|
|
||||||
show-sql: false
|
|
||||||
|
|
||||||
server:
|
|
||||||
port: 8000
|
|
||||||
|
|
||||||
caching:
|
|
||||||
spring:
|
|
||||||
hotelListTTL: 43200
|
|
||||||
guava:
|
|
||||||
hotelItemTTL: 43200
|
|
@ -1,36 +0,0 @@
|
|||||||
SET REFERENTIAL_INTEGRITY FALSE;
|
|
||||||
TRUNCATE TABLE city;
|
|
||||||
TRUNCATE TABLE hotel;
|
|
||||||
SET REFERENTIAL_INTEGRITY TRUE;
|
|
||||||
ALTER TABLE city
|
|
||||||
ALTER COLUMN id RESTART WITH 1;
|
|
||||||
ALTER TABLE hotel
|
|
||||||
ALTER COLUMN id RESTART WITH 1;
|
|
||||||
|
|
||||||
INSERT INTO city(id, name, city_centre_latitude, city_centre_longitude)
|
|
||||||
VALUES (1, 'Amsterdam', 52.368780, 4.903303);
|
|
||||||
INSERT INTO city(id, name, city_centre_latitude, city_centre_longitude)
|
|
||||||
VALUES (2, 'Manchester', 53.481062, -2.237706);
|
|
||||||
|
|
||||||
INSERT INTO hotel(name, deleted, rating, city_id, address, latitude, longitude)
|
|
||||||
VALUES ('Monaghan Hotel', false, 9.2, 1, 'Weesperbuurt en Plantage', 52.364799, 4.908971);
|
|
||||||
INSERT INTO hotel(name, deleted, rating, city_id, address, latitude, longitude)
|
|
||||||
VALUES ('The Thornton Council Hotel', false, 6.3, 1, 'Waterlooplein', 52.3681563, 4.9010029);
|
|
||||||
INSERT INTO hotel(name, deleted, rating, city_id, address, latitude, longitude)
|
|
||||||
VALUES ('McZoe Trescothiks Hotel', false, 9.8, 1, 'Oude Stad, Harlem', 52.379577, 4.633547);
|
|
||||||
INSERT INTO hotel(name, deleted, rating, city_id, address, latitude, longitude)
|
|
||||||
VALUES ('Stay Schmtay Hotel', false, 8.7, 1, 'Jan van Galenstraat', 52.3756755, 4.8668628);
|
|
||||||
INSERT INTO hotel(name, deleted, rating, city_id, address, latitude, longitude)
|
|
||||||
VALUES ('Fitting Image Hotel', false, NULL, 1, 'Staatsliedenbuurt', 52.380936, 4.8708297);
|
|
||||||
INSERT INTO hotel(name, deleted, rating, city_id, address, latitude, longitude)
|
|
||||||
VALUES ('Raymond of Amsterdam Hotel', false, NULL, 1, '22 High Avenue', 52.3773989, 4.8846443);
|
|
||||||
INSERT INTO hotel(name, deleted, rating, city_id, address, latitude, longitude)
|
|
||||||
VALUES ('201 Deansgate Hotel', false, 7.3, 2, '201 Deansgate', 53.4788305, -2.2484721);
|
|
||||||
INSERT INTO hotel(name, deleted, rating, city_id, address, latitude, longitude)
|
|
||||||
VALUES ('Fountain Street Hotel', true, 3.0, 2, '35 Fountain Street', 53.4811298, -2.2402227);
|
|
||||||
INSERT INTO hotel(name, deleted, rating, city_id, address, latitude, longitude)
|
|
||||||
VALUES ('Sunlight House', false, 4.3, 2, 'Little Quay St', 53.4785129, -2.2505943);
|
|
||||||
INSERT INTO hotel(name, deleted, rating, city_id, address, latitude, longitude)
|
|
||||||
VALUES ('St Georges House', false, 9.6, 2, '56 Peter St', 53.477822, -2.2462002);
|
|
||||||
INSERT INTO hotel(name, deleted, rating, city_id, address, latitude, longitude)
|
|
||||||
VALUES ('Marriot Bonvoy', false, 9.6, 1, 'Hans Zimmerstraat', 53.477872, -2.2462003);
|
|
@ -1,71 +0,0 @@
|
|||||||
package com.baeldung.caching.ttl.controller;
|
|
||||||
|
|
||||||
import com.baeldung.caching.ttl.model.Hotel;
|
|
||||||
import com.baeldung.caching.ttl.repository.CityRepository;
|
|
||||||
import com.baeldung.caching.ttl.repository.HotelRepository;
|
|
||||||
import com.booking.testing.SlowTest;
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
import org.junit.jupiter.api.DisplayName;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
|
||||||
import org.springframework.http.MediaType;
|
|
||||||
import org.springframework.test.context.jdbc.Sql;
|
|
||||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
|
||||||
import org.springframework.test.web.servlet.MockMvc;
|
|
||||||
|
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
|
||||||
import static org.hamcrest.Matchers.equalTo;
|
|
||||||
import static org.hamcrest.Matchers.hasSize;
|
|
||||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
|
||||||
|
|
||||||
@ExtendWith(SpringExtension.class)
|
|
||||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
|
|
||||||
@AutoConfigureMockMvc
|
|
||||||
@Sql(executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD, scripts = "classpath:data.sql")
|
|
||||||
@SlowTest
|
|
||||||
class HotelControllerIntegrationTest {
|
|
||||||
@Autowired private MockMvc mockMvc;
|
|
||||||
@Autowired private ObjectMapper mapper;
|
|
||||||
|
|
||||||
@Autowired private HotelRepository repository;
|
|
||||||
@Autowired private CityRepository cityRepository;
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@DisplayName("When all hotels are requested then they are all returned")
|
|
||||||
void whenAllHotelsRequested_thenReturnAllHotels() throws Exception {
|
|
||||||
mockMvc
|
|
||||||
.perform(get("/hotel"))
|
|
||||||
.andExpect(status().is2xxSuccessful())
|
|
||||||
.andExpect(jsonPath("$", hasSize((int) repository.findAll().stream().count())));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@DisplayName("When a hotel is requested by id then the hotel is returned")
|
|
||||||
void whenAGivenHotelsRequested_thenReturnTheHotel() throws Exception {
|
|
||||||
Long hotelId = 1L;
|
|
||||||
Hotel hotel =
|
|
||||||
mapper
|
|
||||||
.readValue(
|
|
||||||
mockMvc
|
|
||||||
.perform(
|
|
||||||
get("/hotel/" + hotelId)
|
|
||||||
.contentType(MediaType.APPLICATION_JSON))
|
|
||||||
.andExpect(status().isOk())
|
|
||||||
.andReturn()
|
|
||||||
.getResponse()
|
|
||||||
.getContentAsString(),
|
|
||||||
Hotel.class);
|
|
||||||
|
|
||||||
assertThat(
|
|
||||||
repository
|
|
||||||
.findById(hotelId)
|
|
||||||
.orElseThrow(() -> new IllegalStateException(String
|
|
||||||
.format("Hotel with id %s does not exist even in repository", hotelId))),
|
|
||||||
equalTo(hotel));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,4 +0,0 @@
|
|||||||
package com.booking.testing;
|
|
||||||
|
|
||||||
public @interface SlowTest {
|
|
||||||
}
|
|
@ -1,15 +0,0 @@
|
|||||||
server:
|
|
||||||
port: 8001
|
|
||||||
|
|
||||||
spring:
|
|
||||||
jpa:
|
|
||||||
open-in-view: true
|
|
||||||
hibernate:
|
|
||||||
ddl-auto: create-drop
|
|
||||||
show-sql: false
|
|
||||||
|
|
||||||
caching:
|
|
||||||
spring:
|
|
||||||
hotelListTTL: 43200
|
|
||||||
guava:
|
|
||||||
hotelItemTTL: 43200
|
|
Loading…
x
Reference in New Issue
Block a user