diff --git a/docker-compose.yml b/docker-compose.yml
index 47579bbaf59..9708195177b 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -1,21 +1,11 @@
services:
- mysql:
- image: mysql:9.1
+ redis:
+ image: redis:7.2
ports:
- - "3306:3306"
- environment:
- - MYSQL_ROOT_PASSWORD=
- - MYSQL_ALLOW_EMPTY_PASSWORD=true
- - MYSQL_USER=petclinic
- - MYSQL_PASSWORD=petclinic
- - MYSQL_DATABASE=petclinic
+ - "6379:6379"
volumes:
- - "./conf.d:/etc/mysql/conf.d:ro"
- postgres:
- image: postgres:17.0
- ports:
- - "5432:5432"
- environment:
- - POSTGRES_PASSWORD=petclinic
- - POSTGRES_USER=petclinic
- - POSTGRES_DB=petclinic
+ - redis-data:/data
+ command: redis-server --appendonly yes
+
+volumes:
+ redis-data:
diff --git a/k8s/db.yml b/k8s/db.yml
index c230ddba2b1..3b71bb656fc 100644
--- a/k8s/db.yml
+++ b/k8s/db.yml
@@ -3,15 +3,12 @@ apiVersion: v1
kind: Secret
metadata:
name: demo-db
-type: servicebinding.io/postgresql
+type: servicebinding.io/redis
stringData:
- type: "postgresql"
- provider: "postgresql"
+ type: "redis"
+ provider: "redis"
host: "demo-db"
- port: "5432"
- database: "petclinic"
- username: "user"
- password: "pass"
+ port: "6379"
---
apiVersion: v1
@@ -20,7 +17,7 @@ metadata:
name: demo-db
spec:
ports:
- - port: 5432
+ - port: 6379
selector:
app: demo-db
@@ -41,33 +38,24 @@ spec:
app: demo-db
spec:
containers:
- - image: postgres:17
- name: postgresql
- env:
- - name: POSTGRES_USER
- valueFrom:
- secretKeyRef:
- name: demo-db
- key: username
- - name: POSTGRES_PASSWORD
- valueFrom:
- secretKeyRef:
- name: demo-db
- key: password
- - name: POSTGRES_DB
- valueFrom:
- secretKeyRef:
- name: demo-db
- key: database
+ - image: redis:7.2
+ name: redis
+ args: ["--appendonly", "yes"]
ports:
- - containerPort: 5432
- name: postgresql
+ - containerPort: 6379
+ name: redis
livenessProbe:
tcpSocket:
- port: postgresql
+ port: redis
readinessProbe:
tcpSocket:
- port: postgresql
+ port: redis
startupProbe:
tcpSocket:
- port: postgresql
+ port: redis
+ volumeMounts:
+ - name: redis-data
+ mountPath: /data
+ volumes:
+ - name: redis-data
+ emptyDir: {}
diff --git a/k8s/petclinic.yml b/k8s/petclinic.yml
index a5677cd06a6..235682ffcc8 100644
--- a/k8s/petclinic.yml
+++ b/k8s/petclinic.yml
@@ -33,7 +33,7 @@ spec:
image: dsyer/petclinic
env:
- name: SPRING_PROFILES_ACTIVE
- value: postgres
+ value: default
- name: SERVICE_BINDING_ROOT
value: /bindings
- name: SPRING_APPLICATION_JSON
diff --git a/pom.xml b/pom.xml
index 27fb9a6bd6a..1e20669f2ed 100644
--- a/pom.xml
+++ b/pom.xml
@@ -50,10 +50,6 @@
org.springframework.boot
spring-boot-starter-cache
-
- org.springframework.boot
- spring-boot-starter-data-jpa
-
org.springframework.boot
spring-boot-starter-web
@@ -77,21 +73,14 @@
reactor-core
-
+
- com.h2database
- h2
- runtime
-
-
- com.mysql
- mysql-connector-j
- runtime
+ org.springframework.boot
+ spring-boot-starter-data-redis
- org.postgresql
- postgresql
- runtime
+ redis.clients
+ jedis
diff --git a/src/main/java/org/springframework/samples/petclinic/config/RedisConfig.java b/src/main/java/org/springframework/samples/petclinic/config/RedisConfig.java
new file mode 100644
index 00000000000..f2871c11e6f
--- /dev/null
+++ b/src/main/java/org/springframework/samples/petclinic/config/RedisConfig.java
@@ -0,0 +1,26 @@
+package org.springframework.samples.petclinic.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.redis.connection.RedisConnectionFactory;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
+import org.springframework.data.redis.serializer.StringRedisSerializer;
+
+/**
+ * Redis configuration for the application.
+ */
+@Configuration
+public class RedisConfig {
+
+ @Bean
+ public RedisTemplate redisTemplate(RedisConnectionFactory connectionFactory) {
+ RedisTemplate template = new RedisTemplate<>();
+ template.setConnectionFactory(connectionFactory);
+ template.setKeySerializer(new StringRedisSerializer());
+ template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
+ template.setHashKeySerializer(new StringRedisSerializer());
+ template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
+ return template;
+ }
+}
diff --git a/src/main/java/org/springframework/samples/petclinic/config/RedisDataInitializer.java b/src/main/java/org/springframework/samples/petclinic/config/RedisDataInitializer.java
new file mode 100644
index 00000000000..9bb173b2e59
--- /dev/null
+++ b/src/main/java/org/springframework/samples/petclinic/config/RedisDataInitializer.java
@@ -0,0 +1,210 @@
+package org.springframework.samples.petclinic.config;
+
+import java.time.LocalDate;
+import java.util.Arrays;
+
+import org.springframework.boot.CommandLineRunner;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.samples.petclinic.owner.Owner;
+import org.springframework.samples.petclinic.owner.Pet;
+import org.springframework.samples.petclinic.owner.PetType;
+import org.springframework.samples.petclinic.owner.RedisOwnerRepository;
+import org.springframework.samples.petclinic.owner.Visit;
+import org.springframework.samples.petclinic.vet.RedisVetRepository;
+import org.springframework.samples.petclinic.vet.Specialty;
+import org.springframework.samples.petclinic.vet.Vet;
+import org.springframework.data.redis.core.RedisTemplate;
+
+/**
+ * Initialize Redis with sample data.
+ */
+@Configuration
+public class RedisDataInitializer {
+
+ @Bean
+ public CommandLineRunner initData(RedisOwnerRepository ownerRepository, RedisVetRepository vetRepository,
+ RedisTemplate redisTemplate) {
+ return args -> {
+ // Clear existing data
+ redisTemplate.delete("owners");
+ redisTemplate.delete("vets");
+ redisTemplate.delete("pet_types");
+ redisTemplate.delete("owner_id_sequence");
+ redisTemplate.delete("vet_id_sequence");
+
+ // Create pet types
+ PetType dog = new PetType();
+ dog.setName("dog");
+
+ PetType cat = new PetType();
+ cat.setName("cat");
+
+ PetType bird = new PetType();
+ bird.setName("bird");
+
+ PetType lizard = new PetType();
+ lizard.setName("lizard");
+
+ PetType snake = new PetType();
+ snake.setName("snake");
+
+ PetType hamster = new PetType();
+ hamster.setName("hamster");
+
+ // Save pet types to Redis
+ redisTemplate.opsForSet().add("pet_types", dog, cat, bird, lizard, snake, hamster);
+
+ // Create specialties
+ Specialty radiology = new Specialty();
+ radiology.setName("radiology");
+
+ Specialty surgery = new Specialty();
+ surgery.setName("surgery");
+
+ Specialty dentistry = new Specialty();
+ dentistry.setName("dentistry");
+
+ // Create vets
+ Vet vet1 = new Vet();
+ vet1.setFirstName("James");
+ vet1.setLastName("Carter");
+ vetRepository.save(vet1);
+
+ Vet vet2 = new Vet();
+ vet2.setFirstName("Helen");
+ vet2.setLastName("Leary");
+ vet2.addSpecialty(radiology);
+ vetRepository.save(vet2);
+
+ Vet vet3 = new Vet();
+ vet3.setFirstName("Linda");
+ vet3.setLastName("Douglas");
+ vet3.addSpecialty(surgery);
+ vet3.addSpecialty(dentistry);
+ vetRepository.save(vet3);
+
+ Vet vet4 = new Vet();
+ vet4.setFirstName("Rafael");
+ vet4.setLastName("Ortega");
+ vet4.addSpecialty(surgery);
+ vetRepository.save(vet4);
+
+ Vet vet5 = new Vet();
+ vet5.setFirstName("Henry");
+ vet5.setLastName("Stevens");
+ vet5.addSpecialty(radiology);
+ vetRepository.save(vet5);
+
+ Vet vet6 = new Vet();
+ vet6.setFirstName("Sharon");
+ vet6.setLastName("Jenkins");
+ vetRepository.save(vet6);
+
+ // Create owners, pets, and visits
+ Owner owner1 = new Owner();
+ owner1.setFirstName("George");
+ owner1.setLastName("Franklin");
+ owner1.setAddress("110 W. Liberty St.");
+ owner1.setCity("Madison");
+ owner1.setTelephone("6085551023");
+
+ Pet pet1 = new Pet();
+ pet1.setName("Leo");
+ pet1.setBirthDate(LocalDate.now().minusYears(2));
+ pet1.setType(cat);
+ owner1.addPet(pet1);
+
+ ownerRepository.save(owner1);
+
+ Owner owner2 = new Owner();
+ owner2.setFirstName("Betty");
+ owner2.setLastName("Davis");
+ owner2.setAddress("638 Cardinal Ave.");
+ owner2.setCity("Sun Prairie");
+ owner2.setTelephone("6085551749");
+
+ Pet pet2 = new Pet();
+ pet2.setName("Basil");
+ pet2.setBirthDate(LocalDate.now().minusYears(1));
+ pet2.setType(hamster);
+ owner2.addPet(pet2);
+
+ ownerRepository.save(owner2);
+
+ Owner owner3 = new Owner();
+ owner3.setFirstName("Eduardo");
+ owner3.setLastName("Rodriquez");
+ owner3.setAddress("2693 Commerce St.");
+ owner3.setCity("McFarland");
+ owner3.setTelephone("6085558763");
+
+ Pet pet3 = new Pet();
+ pet3.setName("Rosy");
+ pet3.setBirthDate(LocalDate.now().minusYears(3));
+ pet3.setType(dog);
+ owner3.addPet(pet3);
+
+ ownerRepository.save(owner3);
+
+ Owner owner4 = new Owner();
+ owner4.setFirstName("Harold");
+ owner4.setLastName("Davis");
+ owner4.setAddress("563 Friendly St.");
+ owner4.setCity("Windsor");
+ owner4.setTelephone("6085553198");
+
+ Pet pet4 = new Pet();
+ pet4.setName("Jewel");
+ pet4.setBirthDate(LocalDate.now().minusYears(1));
+ pet4.setType(dog);
+ owner4.addPet(pet4);
+
+ ownerRepository.save(owner4);
+
+ Owner owner5 = new Owner();
+ owner5.setFirstName("Peter");
+ owner5.setLastName("McTavish");
+ owner5.setAddress("2387 S. Fair Way");
+ owner5.setCity("Madison");
+ owner5.setTelephone("6085552765");
+
+ Pet pet5 = new Pet();
+ pet5.setName("George");
+ pet5.setBirthDate(LocalDate.now().minusYears(4));
+ pet5.setType(snake);
+ owner5.addPet(pet5);
+
+ ownerRepository.save(owner5);
+
+ Owner owner6 = new Owner();
+ owner6.setFirstName("Jean");
+ owner6.setLastName("Coleman");
+ owner6.setAddress("105 N. Lake St.");
+ owner6.setCity("Monona");
+ owner6.setTelephone("6085552654");
+
+ Pet pet6 = new Pet();
+ pet6.setName("Max");
+ pet6.setBirthDate(LocalDate.now().minusYears(2));
+ pet6.setType(cat);
+ owner6.addPet(pet6);
+
+ Pet pet7 = new Pet();
+ pet7.setName("Samantha");
+ pet7.setBirthDate(LocalDate.now().minusYears(1));
+ pet7.setType(cat);
+ owner6.addPet(pet7);
+
+ ownerRepository.save(owner6);
+
+ // Add visits
+ Visit visit1 = new Visit();
+ visit1.setDate(LocalDate.now().minusDays(5));
+ visit1.setDescription("Sneezing");
+ pet7.addVisit(visit1);
+
+ ownerRepository.save(owner6);
+ };
+ }
+}
diff --git a/src/main/java/org/springframework/samples/petclinic/model/BaseEntity.java b/src/main/java/org/springframework/samples/petclinic/model/BaseEntity.java
index 3038bce3a90..caf89929235 100644
--- a/src/main/java/org/springframework/samples/petclinic/model/BaseEntity.java
+++ b/src/main/java/org/springframework/samples/petclinic/model/BaseEntity.java
@@ -17,11 +17,6 @@
import java.io.Serializable;
-import jakarta.persistence.GeneratedValue;
-import jakarta.persistence.GenerationType;
-import jakarta.persistence.Id;
-import jakarta.persistence.MappedSuperclass;
-
/**
* Simple JavaBean domain object with an id property. Used as a base class for objects
* needing this property.
@@ -29,11 +24,8 @@
* @author Ken Krebs
* @author Juergen Hoeller
*/
-@MappedSuperclass
public class BaseEntity implements Serializable {
- @Id
- @GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
public Integer getId() {
diff --git a/src/main/java/org/springframework/samples/petclinic/model/NamedEntity.java b/src/main/java/org/springframework/samples/petclinic/model/NamedEntity.java
index 012e8c4be74..aa569467e3c 100644
--- a/src/main/java/org/springframework/samples/petclinic/model/NamedEntity.java
+++ b/src/main/java/org/springframework/samples/petclinic/model/NamedEntity.java
@@ -15,8 +15,6 @@
*/
package org.springframework.samples.petclinic.model;
-import jakarta.persistence.Column;
-import jakarta.persistence.MappedSuperclass;
import jakarta.validation.constraints.NotBlank;
/**
@@ -27,10 +25,8 @@
* @author Juergen Hoeller
* @author Wick Dynex
*/
-@MappedSuperclass
public class NamedEntity extends BaseEntity {
- @Column(name = "name")
@NotBlank
private String name;
diff --git a/src/main/java/org/springframework/samples/petclinic/model/Person.java b/src/main/java/org/springframework/samples/petclinic/model/Person.java
index 7c3d81a84d9..9ed416e599c 100644
--- a/src/main/java/org/springframework/samples/petclinic/model/Person.java
+++ b/src/main/java/org/springframework/samples/petclinic/model/Person.java
@@ -15,8 +15,6 @@
*/
package org.springframework.samples.petclinic.model;
-import jakarta.persistence.Column;
-import jakarta.persistence.MappedSuperclass;
import jakarta.validation.constraints.NotBlank;
/**
@@ -24,14 +22,11 @@
*
* @author Ken Krebs
*/
-@MappedSuperclass
public class Person extends BaseEntity {
- @Column(name = "first_name")
@NotBlank
private String firstName;
- @Column(name = "last_name")
@NotBlank
private String lastName;
diff --git a/src/main/java/org/springframework/samples/petclinic/owner/Owner.java b/src/main/java/org/springframework/samples/petclinic/owner/Owner.java
index c5ae067dce5..fa5c1c1bc20 100644
--- a/src/main/java/org/springframework/samples/petclinic/owner/Owner.java
+++ b/src/main/java/org/springframework/samples/petclinic/owner/Owner.java
@@ -22,14 +22,6 @@
import org.springframework.samples.petclinic.model.Person;
import org.springframework.util.Assert;
-import jakarta.persistence.CascadeType;
-import jakarta.persistence.Column;
-import jakarta.persistence.Entity;
-import jakarta.persistence.FetchType;
-import jakarta.persistence.JoinColumn;
-import jakarta.persistence.OneToMany;
-import jakarta.persistence.OrderBy;
-import jakarta.persistence.Table;
import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.NotBlank;
@@ -43,26 +35,18 @@
* @author Oliver Drotbohm
* @author Wick Dynex
*/
-@Entity
-@Table(name = "owners")
public class Owner extends Person {
- @Column(name = "address")
@NotBlank
private String address;
- @Column(name = "city")
@NotBlank
private String city;
- @Column(name = "telephone")
@NotBlank
@Pattern(regexp = "\\d{10}", message = "{telephone.invalid}")
private String telephone;
- @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
- @JoinColumn(name = "owner_id")
- @OrderBy("name")
private final List pets = new ArrayList<>();
public String getAddress() {
diff --git a/src/main/java/org/springframework/samples/petclinic/owner/Pet.java b/src/main/java/org/springframework/samples/petclinic/owner/Pet.java
index 1fdc77cec2d..ca1e6fb2679 100644
--- a/src/main/java/org/springframework/samples/petclinic/owner/Pet.java
+++ b/src/main/java/org/springframework/samples/petclinic/owner/Pet.java
@@ -23,16 +23,6 @@
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.samples.petclinic.model.NamedEntity;
-import jakarta.persistence.CascadeType;
-import jakarta.persistence.Column;
-import jakarta.persistence.Entity;
-import jakarta.persistence.FetchType;
-import jakarta.persistence.JoinColumn;
-import jakarta.persistence.ManyToOne;
-import jakarta.persistence.OneToMany;
-import jakarta.persistence.OrderBy;
-import jakarta.persistence.Table;
-
/**
* Simple business object representing a pet.
*
@@ -41,21 +31,13 @@
* @author Sam Brannen
* @author Wick Dynex
*/
-@Entity
-@Table(name = "pets")
public class Pet extends NamedEntity {
- @Column(name = "birth_date")
@DateTimeFormat(pattern = "yyyy-MM-dd")
private LocalDate birthDate;
- @ManyToOne
- @JoinColumn(name = "type_id")
private PetType type;
- @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
- @JoinColumn(name = "pet_id")
- @OrderBy("date ASC")
private final Set visits = new LinkedHashSet<>();
public void setBirthDate(LocalDate birthDate) {
diff --git a/src/main/java/org/springframework/samples/petclinic/owner/PetType.java b/src/main/java/org/springframework/samples/petclinic/owner/PetType.java
index eeea6a758df..8466c87799b 100644
--- a/src/main/java/org/springframework/samples/petclinic/owner/PetType.java
+++ b/src/main/java/org/springframework/samples/petclinic/owner/PetType.java
@@ -17,14 +17,9 @@
import org.springframework.samples.petclinic.model.NamedEntity;
-import jakarta.persistence.Entity;
-import jakarta.persistence.Table;
-
/**
* @author Juergen Hoeller Can be Cat, Dog, Hamster...
*/
-@Entity
-@Table(name = "types")
public class PetType extends NamedEntity {
}
diff --git a/src/main/java/org/springframework/samples/petclinic/owner/RedisOwnerRepository.java b/src/main/java/org/springframework/samples/petclinic/owner/RedisOwnerRepository.java
new file mode 100644
index 00000000000..5db159fe9dd
--- /dev/null
+++ b/src/main/java/org/springframework/samples/petclinic/owner/RedisOwnerRepository.java
@@ -0,0 +1,164 @@
+package org.springframework.samples.petclinic.owner;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageImpl;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.stereotype.Repository;
+
+/**
+ * Redis implementation of the {@link OwnerRepository} interface.
+ */
+@Repository
+public class RedisOwnerRepository implements OwnerRepository {
+
+ private final RedisTemplate redisTemplate;
+ private static final String OWNER_KEY = "owners";
+ private static final String PET_TYPE_KEY = "pet_types";
+
+ public RedisOwnerRepository(RedisTemplate redisTemplate) {
+ this.redisTemplate = redisTemplate;
+ }
+
+ @Override
+ public List findPetTypes() {
+ Set