-
Notifications
You must be signed in to change notification settings - Fork 2
Add caching example with Spring Data Redis #256
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Summary of Changes
Hello @rashidi, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!
This pull request introduces a new, self-contained example project that illustrates the integration of Spring Data Redis for caching within a Spring Boot application. It provides a practical demonstration of configuring Redis as a cache manager and leveraging Spring's @Cacheable
annotation on Spring Data JPA repositories, complete with robust testing using Testcontainers.
Highlights
- New Example Module: I've added a new Gradle module named
data-redis-cache
to demonstrate how to implement caching using Spring Data Redis with Spring Boot and Spring Data JPA. - Caching Configuration: The new module includes a
CacheConfiguration
class that enables Spring's caching mechanism and configuresRedisCacheManager
to use Redis as the caching store. - Cacheable Repository Methods: I've applied
@Cacheable
annotations tofindAll()
andfindById()
methods within theCustomerRepository
to showcase how Spring automatically caches method results, improving performance for repeated queries. - Testcontainers Integration: The example includes comprehensive integration tests using Testcontainers for both PostgreSQL (for the JPA entity) and Redis, ensuring the caching setup works as expected in a realistic environment.
Using Gemini Code Assist
The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.
Invoking Gemini
You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command>
or @gemini-code-assist <command>
. Below is a summary of the supported commands.
Feature | Command | Description |
---|---|---|
Code Review | /gemini review |
Performs a code review for the current pull request in its current state. |
Pull Request Summary | /gemini summary |
Provides a summary of the current pull request in its current state. |
Comment | @gemini-code-assist | Responds in comments when explicitly tagged, both in issue comments and review comments. |
Help | /gemini help |
Displays a list of available commands. |
Customization
To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/
folder in the base of the repository. Detailed instructions can be found here.
Limitations & Feedback
Gemini Code Assist is currently in preview and may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments to provide feedback.
You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.
Footnotes
-
Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution. ↩
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code Review
The code changes introduce a new module demonstrating caching with Spring Data Redis. The review includes fixes for the Customer
entity's equals
and hashCode
methods to prevent NullPointerException
s and ensure correctness with collections. Improvements to the test implementation for better robustness and maintainability have also been suggested.
|
||
@Override | ||
public final boolean equals(Object o) { | ||
return o instanceof Customer another && this.id.equals(another.id); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This implementation will throw a NullPointerException
if this.id
is null
, which is the case for a new entity before it's persisted. You should add a null-check for the id
before calling .equals()
on it to prevent this.
return o instanceof Customer another && this.id.equals(another.id); | |
return o instanceof Customer another && id != null && id.equals(another.id); |
|
||
@Override | ||
public int hashCode() { | ||
return id.hashCode(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This hashCode
implementation will throw a NullPointerException
if id
is null
. Furthermore, for JPA entities with generated IDs, the hash code will change after the entity is persisted. This can cause issues if the entity is stored in a HashSet
or as a key in a HashMap
.
A common and safer practice is to use a constant hash code that doesn't depend on the generated ID, ensuring the hash code is stable throughout the entity's lifecycle.
return id.hashCode(); | |
return getClass().hashCode(); |
|
||
@Bean | ||
public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) { | ||
return RedisCacheManager.create(connectionFactory); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The RedisCacheManager.create()
method has been deprecated since Spring Framework 6.1. It's recommended to use the builder pattern, which provides more flexibility for configuration.
return RedisCacheManager.create(connectionFactory); | |
return RedisCacheManager.builder(connectionFactory).build(); |
@DisplayName("Given the method name is configured as the cache's key Then subsequent retrieval should return the same value as initial retrieval") | ||
void findAll() { | ||
var persisted = customers.findAll(); | ||
var cached = caches.getCache("customers").get("findAll").get(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Directly chaining .get()
can lead to a NullPointerException
if the cache entry for the key "findAll"
does not exist, as caches.getCache("customers").get("findAll")
would return null
. This can make test failures harder to diagnose.
A more robust approach would be to verify the ValueWrapper
is not null before getting its value.
@DisplayName("Given the cache is configured Then subsequent retrieval with the same key should return the same value as initial retrieval") | ||
void findById() { | ||
var persisted = customers.findById(1L).get(); | ||
var cached = caches.getCache("customerById").get(1L).get(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Directly chaining .get()
can lead to a NullPointerException
if the cache entry for the key 1L
does not exist, as caches.getCache("customerById").get(1L)
would return null
. This can make test failures harder to diagnose.
A more robust approach would be to verify the ValueWrapper
is not null before getting its value.
|
No description provided.