Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
FROM maven:3.9.9-eclipse-temurin-24 AS builder
WORKDIR /build
COPY pom.xml .
COPY src ./src
RUN mvn clean package

FROM openjdk:24-jdk

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Verify JDK version choice

JDK 24 is very recent (released in March 2024). Ensure this is the intended version for your project and that all your dependencies are compatible with it.


🌐 Web query:

What is the recommended Java version for Spring Boot applications in 2024?

💡 Result:

Recommended Java Version for Spring Boot Applications in 2024

Minimum and Supported Versions

  • As of 2024, Spring Boot 3.4.x is the current stable version, and it requires at least Java 17[1][2][5].
  • Spring Boot 3.x supports Java versions from 17 up to and including Java 24[1].
  • The underlying Spring Framework 6.2, and upcoming 7.0, are also designed to work with JDK 17 as the baseline, with full support for current LTS (Long-Term Support) releases like Java 17 and Java 21, and compatibility up to Java 23/24[3][4][8].

Recommended Practice

  • Java 21 is the most recent LTS (Long-Term Support) version as of 2024 and is broadly recommended for new Spring Boot applications because it offers the latest language features, performance improvements, and long-term support guarantees[4][8].
  • While Java 17 remains the baseline and is fully supported, upgrading to Java 21 provides advantages such as enhanced performance, new JVM features (e.g., virtual threads), and improved developer productivity[2][8].
  • For environments using Spring Boot with buildpacks or containerization, Liberica JDK is the default Java runtime recommended by Spring, but any TCK-verified distribution of Java 17 or later is suitable[2].

Summary Table

Java Version Status with Spring Boot 3.4.x (2024) Recommended Use
Java 17 Minimum required, fully supported Supported (LTS)
Java 21 Fully supported (LTS) Recommended (LTS)
Java 22–24 Supported Use with caution*

*Java 22–24 are supported, but only Java 17 and 21 are LTS releases, which are preferred for stability and long-term maintenance[1][4].

Conclusion

The best practice for Spring Boot applications in 2024 is to use Java 21 LTS for both new projects and upgrades, unless constrained by environment or dependency requirements. Java 17 remains a viable and supported baseline, but Java 21 offers the best combination of stability, support, and modern Java features[1][4][8].

Citations:


Review JDK Version in Dockerfile

Spring Boot 3.4.x supports Java 17–24, but Java 21 LTS is the recommended runtime for stability and long-term support. JDK 24 is non-LTS and may expose you to shorter support windows.

• File: Dockerfile
Line 7: FROM openjdk:24-jdk
Suggestion: use openjdk:21-jdk (or at least Java 17) unless you explicitly need JDK 24 features and accept its non-LTS lifecycle.

WORKDIR /app
ARG JAR_FILE=target/springboot25-0.0.1-SNAPSHOT.jar
COPY --from=builder /build/${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app/app.jar"]
12 changes: 11 additions & 1 deletion compose.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
services:
redis:
image: 'redis:7-alpine'
ports:
- "6379:6379"
Comment on lines +2 to +5

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Redis service configuration needs improvements

The Redis service is correctly configured but lacks data persistence and security measures. Consider adding:

  1. A volume for data persistence
  2. Password protection for Redis
  3. Health checks to ensure service readiness
 redis:
   image: 'redis:7-alpine'
+  command: redis-server --requirepass ${REDIS_PASSWORD}
   ports:
     - "6379:6379"
+  volumes:
+    - redis-data:/data
+  healthcheck:
+    test: ["CMD", "redis-cli", "ping"]
+    interval: 5s
+    timeout: 5s
+    retries: 5

postgres:
image: 'postgres:latest'
container_name: 'postgres-db'
Expand All @@ -9,13 +13,19 @@ services:
ports:
- '5432:5432'
app:
build:
context: .
dockerfile: Dockerfile
image: 'catinder:latest'
depends_on:
- redis
- postgres
image: 'catinder:latest'
env_file:
- .env
environment:
- GOOGLE_CLIENT_ID=${GOOGLE_CLIENT_ID}
- GOOGLE_CLIENT_SECRET=${GOOGLE_CLIENT_SECRET}
- SPRING_REDIS_HOST=redis
- SPRING_REDIS_PORT=6379
Comment on lines +28 to +29

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add Redis password configuration

If you implement password protection for Redis as suggested above, you'll need to add the password to the application's environment variables.

      - SPRING_REDIS_HOST=redis
      - SPRING_REDIS_PORT=6379
+     - SPRING_REDIS_PASSWORD=${REDIS_PASSWORD}

Comment on lines +16 to +29

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Don't forget to add volume definition

If you add the Redis volume as suggested, remember to define it at the bottom of the compose file.

    ports:
      - "8080:8080"

+volumes:
+  redis-data:

Committable suggestion skipped: line range outside the PR's diff.

ports:
- "8080:8080"
25 changes: 19 additions & 6 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,12 @@
<artifactId>flyway-database-postgresql</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<!-- <dependency>-->
<!-- <groupId>org.springframework.boot</groupId>-->
<!-- <artifactId>spring-boot-devtools</artifactId>-->
<!-- <scope>runtime</scope>-->
<!-- <optional>true</optional>-->
<!-- </dependency>-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-docker-compose</artifactId>
Expand Down Expand Up @@ -148,6 +148,19 @@
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>3.1.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
Comment on lines +151 to +159

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Align Redis dependency version with Spring Boot BOM

Specifying <version>3.1.2</version> for spring-boot-starter-data-redis conflicts with the parent spring-boot-starter-parent version (3.4.4). Remove the explicit version to let the BOM manage dependency alignment:

-<dependency>
-  <groupId>org.springframework.boot</groupId>
-  <artifactId>spring-boot-starter-data-redis</artifactId>
-  <version>3.1.2</version>
-</dependency>
+<dependency>
+  <groupId>org.springframework.boot</groupId>
+  <artifactId>spring-boot-starter-data-redis</artifactId>
+</dependency>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>3.1.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;

@SpringBootApplication
@EnableWebSecurity
@EnableCaching
public class Springboot25Application {

public static void main(String[] args) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package org.example.springboot25.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;

import java.time.Duration;

@Configuration
public class RedisCacheConfig {

@Bean
public RedisCacheManager cacheManager(LettuceConnectionFactory cf) {
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(10))
.disableCachingNullValues();

return RedisCacheManager.builder(cf)
.cacheDefaults(config)
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

import java.io.Serializable;
import java.util.List;

@Controller // 🔹 Detta gör att Spring vet att den ska hantera HTML-sidor
public class CatPhotoViewController {
public class CatPhotoViewController implements Serializable {

private final CatPhotoService catPhotoService;

Expand All @@ -25,4 +26,3 @@ public String showCatPhotos(Model model) {
return "catphoto/catphotos"; // 🔹 Returnerar en HTML-sida som heter "catphotos.html"
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,12 @@ public CatViewController(CatService catService, UserService userService) {
this.catService = catService;
this.userService = userService;
}

@GetMapping
public String showAllCats(Model model) {
List<CatOutputDTO> cats = catService.getAllCats();
model.addAttribute("cats", cats);
return "cat/cats-list";
}

@GetMapping("/{catId}")
public String showCatDetail(@PathVariable Long catId, Model model, Principal principal) {
CatOutputDTO cat = catService.getCatDtoById(catId);
Expand Down
21 changes: 20 additions & 1 deletion src/main/java/org/example/springboot25/dto/CatOutputDTO.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
package org.example.springboot25.dto;

public class CatOutputDTO {
import jakarta.persistence.CascadeType;
import jakarta.persistence.OneToMany;
import org.example.springboot25.entities.CatPhoto;
Comment on lines +3 to +5

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

JPA annotations are incorrectly used in a DTO class.

DTOs shouldn't contain JPA annotations like @OneToMany. These annotations are meant for JPA entity classes, not for data transfer objects.

Remove JPA annotations from the DTO class. They should be in the entity class instead:

-import jakarta.persistence.CascadeType;
-import jakarta.persistence.OneToMany;
import org.example.springboot25.entities.CatPhoto;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import jakarta.persistence.CascadeType;
import jakarta.persistence.OneToMany;
import org.example.springboot25.entities.CatPhoto;
import org.example.springboot25.entities.CatPhoto;


import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

public class CatOutputDTO implements Serializable {
private Long catId;
private String catName;
private String catProfilePicture;
Expand All @@ -10,6 +18,17 @@ public class CatOutputDTO {
private String catPersonality;
private Long userId;

@OneToMany(mappedBy = "catPhotoCat", cascade = CascadeType.ALL, orphanRemoval = true)
private List<CatPhoto> catPhotos = new ArrayList<>();
Comment on lines +21 to +22

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

JPA annotation in DTO class will cause runtime errors.

The @OneToMany annotation doesn't belong in a DTO and will likely cause runtime errors, especially since the DTO isn't managed by a JPA entity manager.

Remove the JPA annotation and simplify the property declaration:

-    @OneToMany(mappedBy = "catPhotoCat", cascade = CascadeType.ALL, orphanRemoval = true)
     private List<CatPhoto> catPhotos = new ArrayList<>();
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@OneToMany(mappedBy = "catPhotoCat", cascade = CascadeType.ALL, orphanRemoval = true)
private List<CatPhoto> catPhotos = new ArrayList<>();
private List<CatPhoto> catPhotos = new ArrayList<>();


public List<CatPhoto> getCatPhotos() {
return catPhotos;
}

public void setCatPhotos(List<CatPhoto> catPhotos) {
this.catPhotos = catPhotos;
}
Comment on lines +24 to +30

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

CatPhoto entity is likely not Serializable.

If CatPhoto doesn't implement Serializable, this will cause serialization errors when Redis tries to cache objects containing CatPhoto instances.

Verify that the CatPhoto class implements Serializable:


🏁 Script executed:

#!/bin/bash
# Check if CatPhoto implements Serializable
grep -A 2 "class CatPhoto" src/main/java/org/example/springboot25/entities/CatPhoto.java

Length of output: 123


CatPhoto must implement Serializable for Redis caching

CatPhoto doesn’t implement java.io.Serializable, so attempting to cache DTOs containing CatPhoto instances in Redis will fail at runtime. Please update the entity to implement Serializable and define a serialVersionUID.

• File to update:

  • src/main/java/org/example/springboot25/entities/CatPhoto.java

• Suggested diff:

-public class CatPhoto {
+public class CatPhoto implements Serializable {
+    private static final long serialVersionUID = 1L;

Committable suggestion skipped: line range outside the PR's diff.


public Long getCatId() {
return catId;
}
Expand Down
4 changes: 3 additions & 1 deletion src/main/java/org/example/springboot25/entities/Cat.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import org.springframework.data.redis.core.RedisHash;

import java.io.Serializable;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add serialVersionUID for Serializable implementation

While implementing Serializable is necessary for Redis caching, it's best practice to include a serialVersionUID field to ensure version compatibility during serialization/deserialization.

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

@Entity
-public class Cat implements Serializable {
+public class Cat implements Serializable {
+    private static final long serialVersionUID = 1L;

Also applies to: 15-15

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

@Entity
public class Cat {
public class Cat implements Serializable {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/org/example/springboot25/entities/User.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@
import jakarta.validation.constraints.NotNull;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

@Entity
@Table(name = "app_user")
public class User {
public class User implements Serializable {

private static final BCryptPasswordEncoder ENCODER = new BCryptPasswordEncoder();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import org.example.springboot25.entities.CatPhoto;
import org.example.springboot25.repository.CatPhotoRepository;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import java.util.List;
Expand Down Expand Up @@ -50,4 +51,4 @@ public boolean deleteCatPhoto(Long id) {
}
return false;
}
}
}
11 changes: 11 additions & 0 deletions src/main/java/org/example/springboot25/service/CatService.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.WebDataBinder;
Expand Down Expand Up @@ -93,6 +96,7 @@ public Cat updateCat(Long catId, Cat catDetails) {
}).orElseThrow(() -> new NotFoundException("Cat not found with id " + catId));
}

@CacheEvict(cacheNames = "deleteCatById", key = "#catId")
public void deleteCatById(Long catId) {
Comment on lines +99 to 100

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Incorrect cache key configuration for deletion operations.

The @CacheEvict annotation uses "deleteCatById" as the cache name, but the corresponding @Cacheable methods use different cache names. This mismatch means that deleting a cat won't properly clear the cached items.

Align the cache names with the cacheable operations:

-    @CacheEvict(cacheNames = "deleteCatById", key = "#catId")
+    @CacheEvict(cacheNames = {"catById", "allCats", "allCatsByUser", "allCatsByName"}, allEntries = true)
     public void deleteCatById(Long catId) {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@CacheEvict(cacheNames = "deleteCatById", key = "#catId")
public void deleteCatById(Long catId) {
@CacheEvict(cacheNames = {"catById", "allCats", "allCatsByUser", "allCatsByName"}, allEntries = true)
public void deleteCatById(Long catId) {

if (!catRepository.existsById(catId)) {
throw new NotFoundException("Cat not found with id " + catId);
Expand All @@ -101,6 +105,7 @@ public void deleteCatById(Long catId) {
catRepository.deleteById(catId);
}

@CacheEvict(cacheNames = "deleteCat", key = "#catId")
public void deleteCat(Long catId) {
Comment on lines +108 to 109

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Similar cache configuration issue with deleteCat method.

The @CacheEvict annotation here has the same issue as the previous method, using a different cache name than the cacheable methods.

Correct the cache eviction configuration:

-    @CacheEvict(cacheNames = "deleteCat", key = "#catId")
+    @CacheEvict(cacheNames = {"catById", "allCats", "allCatsByUser", "allCatsByName"}, allEntries = true)
     public void deleteCat(Long catId) {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@CacheEvict(cacheNames = "deleteCat", key = "#catId")
public void deleteCat(Long catId) {
@CacheEvict(cacheNames = {"catById", "allCats", "allCatsByUser", "allCatsByName"}, allEntries = true)
public void deleteCat(Long catId) {

deleteCatById(catId);
}
Expand All @@ -109,18 +114,22 @@ public void deleteCat(Long catId) {
// Externa metoder (DTO)
// ========================

@Cacheable(cacheNames = "allCats")
public List<CatOutputDTO> getAllCats() {
return catRepository.findAll().stream().map(catMapper::toDto).toList();
}

@Cacheable(cacheNames = "allCatsByUser", key = "#user")
public List<CatOutputDTO> getAllCatsByUser(User user) {
Comment on lines +122 to 123

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Potential serialization issue with User parameter in cache key.

The User object is used as a cache key but may not properly implement Serializable or have a suitable hashCode() implementation for cache key usage.

Use a more stable identifier for the cache key:

-    @Cacheable(cacheNames = "allCatsByUser", key = "#user")
+    @Cacheable(cacheNames = "allCatsByUser", key = "#user.userId")
     public List<CatOutputDTO> getAllCatsByUser(User user) {

Also, verify that the User class implements Serializable properly:


🏁 Script executed:

#!/bin/bash
# Check if User implements Serializable
grep -A 2 "class User" src/main/java/org/example/springboot25/entities/User.java

Length of output: 213


Use a stable identifier for the cache key
We verified that User does implement Serializable, but using the entire User object as a cache key can lead to large keys and unintended equals()/hashCode() behavior. Switch to a simple, stable identifier:

• File: src/main/java/org/example/springboot25/service/CatService.java (lines 122–123)

-    @Cacheable(cacheNames = "allCatsByUser", key = "#user")
+    @Cacheable(cacheNames = "allCatsByUser", key = "#user.userId")
     public List<CatOutputDTO> getAllCatsByUser(User user) {

Also, if you ever need to use complex types as keys in the future, ensure they have consistent equals()/hashCode() implementations.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@Cacheable(cacheNames = "allCatsByUser", key = "#user")
public List<CatOutputDTO> getAllCatsByUser(User user) {
@Cacheable(cacheNames = "allCatsByUser", key = "#user.userId")
public List<CatOutputDTO> getAllCatsByUser(User user) {

return catRepository.findAllByUserCatOwner(user).stream().map(catMapper::toDto).toList();
}

@Cacheable(cacheNames = "allCatsByName", key = "#name")
public List<CatOutputDTO> getCatsByName(String name) {
return catRepository.findByCatNameContainingIgnoreCase(name).stream().map(catMapper::toDto).toList();
}

@Cacheable(cacheNames = "catById", key = "#id")
public CatOutputDTO getCatDtoById(Long id) {
return catMapper.toDto(findCatById(id));
}
Expand All @@ -136,13 +145,15 @@ public Cat addCat(Cat cat) {
return saveCat(cat);
}

@CachePut(cacheNames = "putCat", key = "#catId")
public CatOutputDTO updateCat(Long catId, CatUpdateDTO dto) {
Comment on lines +148 to 149

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Cache update strategy needs improvement for putCat operation.

The @CachePut annotation with cache name "putCat" doesn't update entries in the other caches like "allCats", "allCatsByUser", etc., which leads to stale data in those caches.

Either evict all entries from all caches (slower) or update the specific entry in the appropriate cache (more efficient):

-    @CachePut(cacheNames = "putCat", key = "#catId")
+    @CachePut(cacheNames = "catById", key = "#catId")
+    @CacheEvict(cacheNames = {"allCats", "allCatsByUser", "allCatsByName"}, allEntries = true)
     public CatOutputDTO updateCat(Long catId, CatUpdateDTO dto) {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@CachePut(cacheNames = "putCat", key = "#catId")
public CatOutputDTO updateCat(Long catId, CatUpdateDTO dto) {
@CachePut(cacheNames = "catById", key = "#catId")
@CacheEvict(cacheNames = {"allCats", "allCatsByUser", "allCatsByName"}, allEntries = true)
public CatOutputDTO updateCat(Long catId, CatUpdateDTO dto) {

Cat cat = findCatById(catId);
log.info("Updating cat: {}", cat.getCatName());
catMapper.updateCatFromDto(dto, cat);
return catMapper.toDto(saveCat(cat));
}

@CachePut(cacheNames = "patchCat", key = "#catId")
public CatOutputDTO partialUpdateCat(Long catId, Map<String, Object> updates) {
Comment on lines +156 to 157

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Same cache update issue with partialUpdateCat method.

Similar to the previous issue, this method also uses a different cache name "patchCat" that doesn't align with other cache names.

Update the cache configuration:

-    @CachePut(cacheNames = "patchCat", key = "#catId")
+    @CachePut(cacheNames = "catById", key = "#catId")
+    @CacheEvict(cacheNames = {"allCats", "allCatsByUser", "allCatsByName"}, allEntries = true)
     public CatOutputDTO partialUpdateCat(Long catId, Map<String, Object> updates) {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@CachePut(cacheNames = "patchCat", key = "#catId")
public CatOutputDTO partialUpdateCat(Long catId, Map<String, Object> updates) {
@CachePut(cacheNames = "catById", key = "#catId")
@CacheEvict(cacheNames = {"allCats", "allCatsByUser", "allCatsByName"}, allEntries = true)
public CatOutputDTO partialUpdateCat(Long catId, Map<String, Object> updates) {

Cat cat = findCatById(catId);

Expand Down
14 changes: 14 additions & 0 deletions src/main/resources/application-dev.properties
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,17 @@ spring.config.import=optional:file:.env[.properties]
server.servlet.session.persistent=false
catapi.key=${CATAPI_KEY}

# Enable caching
spring.cache.type=redis

# Toggle Redis
spring.redis.enabled=true
spring.data.redis.repositories.enabled=false

# Redis connection details
spring.data.redis.host=${SPRING_REDIS_HOST:localhost}
spring.data.redis.port=${SPRING_REDIS_PORT:6379}
Comment on lines +20 to +21

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Use correct Redis connection properties

Spring Boot binds spring.redis.host and spring.redis.port (without data) for Redis connections. Update as follows:

-spring.data.redis.host=${SPRING_REDIS_HOST:localhost}
-spring.data.redis.port=${SPRING_REDIS_PORT:6379}
+spring.redis.host=${SPRING_REDIS_HOST:localhost}
+spring.redis.port=${SPRING_REDIS_PORT:6379}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
spring.data.redis.host=${SPRING_REDIS_HOST:localhost}
spring.data.redis.port=${SPRING_REDIS_PORT:6379}
spring.redis.host=${SPRING_REDIS_HOST:localhost}
spring.redis.port=${SPRING_REDIS_PORT:6379}


spring.datasource.url=jdbc:postgresql://postgres:5432/mydatabase
spring.datasource.username=myuser
spring.datasource.password=secret