-
Notifications
You must be signed in to change notification settings - Fork 9
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
feat : adds dynamic specification #1544
Conversation
WalkthroughThe pull request introduces several modifications to the Changes
Possibly related PRs
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
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.
Actionable comments posted: 12
🧹 Outside diff range and nitpick comments (12)
jpa/keyset-pagination/boot-data-window-pagination/src/main/java/com/example/keysetpagination/repositories/CustomRepository.java (2)
9-11
: Enhance interface documentation with generic type detailsThe interface documentation could be more descriptive about the generic type parameter and its purpose.
/** - * Custom repository interface for efficient keyset pagination for any entities. + * Custom repository interface for efficient keyset pagination. + * + * @param <T> the domain type the repository manages + * @see org.springframework.data.domain.Window */
14-23
: Fix typo and enhance method documentationThe method documentation has a typo and could benefit from more detailed parameter descriptions.
/** * Finds all entities matching the given specification using keyset pagination. * - * @param spec The specification to filter entites - * @param pageRequest The pagination information - * @param scrollPosition The current position in the result set - * @param entityClass The entity class on which operation should occur + * @param spec The specification to filter entities using JPA criteria + * @param pageRequest The pagination information containing size and sort details + * @param scrollPosition The current position in the result set for continuous scrolling + * @param entityClass The class type of the entity being queried * @return A window containing the paginated results */jpa/keyset-pagination/boot-data-window-pagination/src/test/java/com/example/keysetpagination/web/controllers/AnimalControllerIT.java (4)
80-94
: Consider enhancing type safety and test coverage.While the test structure is good, there are a few potential improvements:
- The
CustomWindow
deserialization could be more type-safe- The test could verify more response fields
Consider this improvement:
- CustomWindow<Map<String, String>> window = objectMapper.readValue(contentAsString, CustomWindow.class); + CustomWindow<Animal> window = objectMapper.readValue( + contentAsString, + objectMapper.getTypeFactory().constructParametricType(CustomWindow.class, Animal.class) + );Also consider adding assertions for:
- Response content type
- Pagination metadata (pageSize, totalElements)
- Sort order of results
Also applies to: 106-124
131-153
: Enhance test assertions for better coverage.While the test verifies the count of results, it could be more thorough in validating the actual content.
Consider adding these assertions:
.andExpect(status().isOk()) .andExpect(jsonPath("$.content", hasSize(4))) +.andExpect(jsonPath("$.content[*].type").value(not(containsInAnyOrder("Mammal", "Bird")))) +.andExpect(jsonPath("$.content[*].type").value(containsInAnyOrder("Fish", "Reptile", "Amphibian", "Reptile"))) .andExpect(jsonPath("$.last", is(true)));
158-172
: Update test name to match implementation.The test name suggests it's testing non-existent type, but it's actually testing non-existent name.
Consider either:
- Renaming the test to match its implementation:
-void shouldReturnEmptyResultForNonExistentType() +void shouldReturnEmptyResultForNonExistentName()
- Or modifying the test to match its name:
-"field": "name", +"field": "type",Also consider adding assertions for:
- Response content type
- Pagination metadata
Line range hint
80-172
: Consider adding test utility methods for search criteria.The search criteria JSON appears in multiple tests. Consider creating utility methods to improve maintainability and reduce duplication.
Example implementation:
private String createSearchCriteria(String operator, String field, String... values) { return String.format(""" [ { "queryOperator": "%s", "field": "%s", "values": %s } ] """, operator, field, objectMapper.writeValueAsString(Arrays.asList(values)) ); }This would make the tests more maintainable and less prone to JSON formatting errors.
jpa/keyset-pagination/boot-data-window-pagination/src/main/java/com/example/keysetpagination/model/query/SearchCriteria.java (1)
10-14
: Consider adding @builder for better object construction.While the current implementation with @AllArgsConstructor works fine for three fields, consider adding @builder for more flexible and maintainable object construction, especially if more fields are added in the future.
@Setter @Getter @AllArgsConstructor @NoArgsConstructor +@Builder public class SearchCriteria {
jpa/keyset-pagination/boot-data-window-pagination/src/main/java/com/example/keysetpagination/repositories/AnimalRepository.java (1)
6-6
: Good architectural improvement!The shift from
CustomAnimalRepository
toCustomRepository<Animal>
aligns well with the PR objective of adding dynamic specification support. This change improves code reusability by making the repository implementation more generic.jpa/keyset-pagination/boot-data-window-pagination/src/main/java/com/example/keysetpagination/model/query/QueryOperator.java (1)
1-19
: Consider adding validation for operator compatibilityThe current implementation might allow invalid combinations of operators and field types (e.g., LIKE with numeric fields).
Consider implementing a validation layer that:
- Validates operator compatibility with field types
- Provides clear error messages for invalid combinations
- Offers type-safe builder patterns for constructing queries
Would you like me to provide an example implementation of the validation layer?
jpa/keyset-pagination/boot-data-window-pagination/src/main/java/com/example/keysetpagination/web/controllers/AnimalController.java (1)
63-63
: Consider the implications of changing the HTTP method from GET to POSTSwitching the
/search
endpoint from@GetMapping
to@PostMapping
changes the way clients interact with the API. WhilePOST
is suitable for complex search criteria sent in the request body, ensure this aligns with RESTful principles and that clients are updated accordingly. Also, be aware that usingPOST
may affect caching mechanisms sinceGET
requests are cacheable by default.jpa/keyset-pagination/boot-data-window-pagination/src/main/java/com/example/keysetpagination/repositories/CustomRepositoryImpl.java (1)
99-99
: Handle sorting on nested propertiesThe current implementation
root.get(order.getProperty())
does not support sorting on nested properties (e.g.,"user.name"
). Consider enhancing the sorting logic to handle nested attributes if needed.jpa/keyset-pagination/boot-data-window-pagination/src/main/java/com/example/keysetpagination/services/EntitySpecification.java (1)
27-36
: UseList<SearchCriteria>
Instead of Array for FlexibilityUsing a
List
instead of an array forsearchCriteriaArray
provides more flexibility and aligns with common Java practices.Apply this diff to update the method signatures and usages:
-public Specification<T> specificationBuilder(SearchCriteria[] searchCriteriaArray, Class<T> entityClass) { +public Specification<T> specificationBuilder(List<SearchCriteria> searchCriteriaList, Class<T> entityClass) { validateMetadata(searchCriteriaList, entityClass); return (root, query, criteriaBuilder) -> { - return Stream.of(searchCriteriaArray) + return searchCriteriaList.stream() .map(entry -> createPredicate(entry, root, criteriaBuilder)) .reduce(criteriaBuilder::and) .orElse(criteriaBuilder.conjunction()); }; } -private void validateMetadata(SearchCriteria[] searchCriteriaArray, Class<T> entityClass) { +private void validateMetadata(List<SearchCriteria> searchCriteriaList, Class<T> entityClass) { // ... - for (SearchCriteria searchCriteria : searchCriteriaArray) { + for (SearchCriteria searchCriteria : searchCriteriaList) { // ... } }This change improves the method's usability and integrates seamlessly with stream operations.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (14)
jpa/keyset-pagination/boot-data-window-pagination/pom.xml
(2 hunks)jpa/keyset-pagination/boot-data-window-pagination/src/main/java/com/example/keysetpagination/WindowPaginationApplication.java
(1 hunks)jpa/keyset-pagination/boot-data-window-pagination/src/main/java/com/example/keysetpagination/model/query/QueryOperator.java
(1 hunks)jpa/keyset-pagination/boot-data-window-pagination/src/main/java/com/example/keysetpagination/model/query/SearchCriteria.java
(1 hunks)jpa/keyset-pagination/boot-data-window-pagination/src/main/java/com/example/keysetpagination/repositories/AnimalRepository.java
(1 hunks)jpa/keyset-pagination/boot-data-window-pagination/src/main/java/com/example/keysetpagination/repositories/CustomAnimalRepositoryImpl.java
(0 hunks)jpa/keyset-pagination/boot-data-window-pagination/src/main/java/com/example/keysetpagination/repositories/CustomRepository.java
(1 hunks)jpa/keyset-pagination/boot-data-window-pagination/src/main/java/com/example/keysetpagination/repositories/CustomRepositoryImpl.java
(1 hunks)jpa/keyset-pagination/boot-data-window-pagination/src/main/java/com/example/keysetpagination/services/AnimalService.java
(3 hunks)jpa/keyset-pagination/boot-data-window-pagination/src/main/java/com/example/keysetpagination/services/AnimalSpecifications.java
(0 hunks)jpa/keyset-pagination/boot-data-window-pagination/src/main/java/com/example/keysetpagination/services/EntitySpecification.java
(1 hunks)jpa/keyset-pagination/boot-data-window-pagination/src/main/java/com/example/keysetpagination/web/controllers/AnimalController.java
(2 hunks)jpa/keyset-pagination/boot-data-window-pagination/src/test/java/com/example/keysetpagination/TestWindowPaginationApplication.java
(1 hunks)jpa/keyset-pagination/boot-data-window-pagination/src/test/java/com/example/keysetpagination/web/controllers/AnimalControllerIT.java
(2 hunks)
💤 Files with no reviewable changes (2)
- jpa/keyset-pagination/boot-data-window-pagination/src/main/java/com/example/keysetpagination/repositories/CustomAnimalRepositoryImpl.java
- jpa/keyset-pagination/boot-data-window-pagination/src/main/java/com/example/keysetpagination/services/AnimalSpecifications.java
✅ Files skipped from review due to trivial changes (1)
- jpa/keyset-pagination/boot-data-window-pagination/src/main/java/com/example/keysetpagination/WindowPaginationApplication.java
🔇 Additional comments (13)
jpa/keyset-pagination/boot-data-window-pagination/src/test/java/com/example/keysetpagination/TestWindowPaginationApplication.java (2)
6-6
: LGTM! Class name change maintains consistency
The rename from TestApplication
to TestWindowPaginationApplication
aligns well with the main application class rename and provides better context about its purpose.
9-12
: LGTM! Verify main application class reference
The update to use WindowPaginationApplication::main
is correct. Let's verify the referenced class exists:
✅ Verification successful
Main application class reference is correctly used
The WindowPaginationApplication
class exists at the expected location in the codebase, confirming that the reference to WindowPaginationApplication::main
in the test class is valid.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Verify the existence of WindowPaginationApplication class
# Expected: Find the main application class definition
rg -l "class WindowPaginationApplication" --type java
Length of output: 181
jpa/keyset-pagination/boot-data-window-pagination/src/main/java/com/example/keysetpagination/repositories/CustomRepository.java (1)
22-23
: LGTM! Well-designed generic repository interface
The interface is well-structured and follows Spring Data JPA best practices:
- Generic type parameter allows for reuse across different entities
- Uses Spring's Window-based pagination for efficient keyset pagination
- Leverages JPA Specification for flexible querying
jpa/keyset-pagination/boot-data-window-pagination/pom.xml (2)
88-88
: LGTM! Good version management practice
Using the property reference instead of hardcoded version improves maintainability and follows Maven best practices.
25-25
: Verify compatibility with Spring Boot 3.4.0
Good practice to externalize the version to a property. However, since the project uses Spring Boot 3.4.0 (milestone), we should verify compatibility between Spring Boot 3.4.0 and datasource-micrometer 1.0.6.
✅ Verification successful
Let me gather more information about the Spring Boot version compatibility by checking the project's dependencies.
Let me try a different approach to check the dependencies and version compatibility.
Version 1.0.6 is compatible with Spring Boot 3.x
The datasource-micrometer-spring-boot version 1.0.6 is the latest stable release and is compatible with Spring Boot 3.x. The project correctly uses Spring Boot 3.4.0-M1 (from spring-milestones repository) and has proper repository configuration for milestone releases. The version externalization to properties follows Maven best practices.
- The project correctly includes required Spring Boot dependencies for the datasource-micrometer integration:
- spring-boot-starter-actuator
- spring-boot-starter-data-jpa
- datasource-micrometer-spring-boot
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Check compatibility and latest versions
# Check latest version of datasource-micrometer-spring-boot
curl -s "https://search.maven.org/solrsearch/select?q=g:net.ttddyy.observation+AND+a:datasource-micrometer-spring-boot&rows=1&wt=json" | jq -r '.response.docs[0].latestVersion'
# Check its dependencies for Spring Boot version compatibility
curl -s "https://search.maven.org/solrsearch/select?q=g:net.ttddyy.observation+AND+a:datasource-micrometer-spring-boot+AND+v:1.0.6&wt=json" | jq -r '.response.docs[0].deps'
Length of output: 364
Script:
#!/bin/bash
# Check Spring Boot parent version and dependencies
rg -U "parent|spring-boot" pom.xml
# Check if there are any existing datasource-micrometer dependencies
rg -U "datasource-micrometer" pom.xml
Length of output: 176
Script:
#!/bin/bash
# Use cat to view the full pom.xml content
cat "jpa/keyset-pagination/boot-data-window-pagination/pom.xml"
Length of output: 15779
jpa/keyset-pagination/boot-data-window-pagination/src/main/java/com/example/keysetpagination/model/query/SearchCriteria.java (1)
14-14
: Verify the implementation in dependent classes.
Let's verify how this class is being used in AnimalService and AnimalController to ensure proper implementation of the specification pattern.
✅ Verification successful
SearchCriteria is properly integrated in the codebase
The implementation shows correct usage of SearchCriteria across the codebase:
- In EntitySpecification, it's used to build dynamic JPA specifications with proper validation of entity fields
- In AnimalController, it's correctly received as an array in the search endpoint and passed to the service layer
- In AnimalService, it's properly utilized to create specifications for dynamic querying
- The pattern is consistently implemented across other entities (e.g., Actor) following the same structure
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Check the implementation in related files
# Check AnimalService implementation
echo "Checking AnimalService implementation:"
rg -A 10 "SearchCriteria.*\[" --type java
# Check AnimalController implementation
echo "Checking AnimalController endpoint:"
ast-grep --pattern 'class AnimalController {
$$$
@PostMapping($$$)
$$$searchAnimals($$$SearchCriteria$$$) {
$$$
}
$$$
}'
Length of output: 13361
jpa/keyset-pagination/boot-data-window-pagination/src/main/java/com/example/keysetpagination/repositories/AnimalRepository.java (1)
6-6
: Verify the repository migration
Since this is a breaking change from CustomAnimalRepository
to CustomRepository<Animal>
, let's verify:
- The existence of the new generic repository
- The successful migration of all required methods
✅ Verification successful
Let me gather more information about the CustomRepository implementation to verify the migration.
Let me do one final check to look for the CustomRepository interface and any custom methods it might contain.
Migration to CustomRepository is properly implemented
The verification shows that:
- The
CustomRepository<T>
interface exists with a well-defined generic contract for keyset pagination - A complete implementation
CustomRepositoryImpl<T>
is present with all required functionality - No references to the old
CustomAnimalRepository
remain in the codebase - All repository usage in
AnimalService
and tests only uses standard JPA methods
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Verify the repository migration and its impact
# Check if CustomRepository exists and its methods
echo "Checking CustomRepository definition:"
ast-grep --pattern 'interface CustomRepository<$_> {
$$$
}'
# Check for any remaining references to CustomAnimalRepository
echo -e "\nChecking for any remaining references to CustomAnimalRepository:"
rg "CustomAnimalRepository"
# Look for potential usage patterns that might need updates
echo -e "\nChecking for potential usage patterns that might need updates:"
rg -A 3 "Animal.*Repository\."
Length of output: 3100
Script:
#!/bin/bash
# Check for CustomRepository implementation and its methods
echo "Checking for CustomRepository implementation:"
fd -e java -x cat {} \; | ast-grep --pattern 'interface CustomRepository<$_> {
$$$
}'
# Check for any previous implementation of repository methods
echo -e "\nChecking for previous implementation files:"
fd -e java -x cat {} \; | ast-grep --pattern 'class $_ implements CustomAnimalRepository {
$$$
}'
# Check for any custom repository methods being used
echo -e "\nChecking for custom repository method usage:"
rg -A 2 "animalRepository\." --type java
Length of output: 4984
Script:
#!/bin/bash
# Find and show the content of any file containing CustomRepository
echo "Looking for CustomRepository definition:"
fd -e java -x grep -l "CustomRepository" {} \; | xargs cat
# Check for any custom methods being used beyond standard JPA methods
echo -e "\nChecking for any custom method implementations:"
fd -e java -x grep -A 5 "class.*implements.*Repository.*{" {} \;
Length of output: 7486
jpa/keyset-pagination/boot-data-window-pagination/src/main/java/com/example/keysetpagination/web/controllers/AnimalController.java (3)
60-61
: Good addition of the 404 response code in API documentation
Including the @ApiResponse
with response code 404
improves the API documentation by explicitly stating the scenario when no animals match the search criteria.
70-71
: Appropriate use of @RequestBody
for dynamic search criteria
Accepting an array of SearchCriteria
objects in the request body allows for flexible and dynamic querying. This approach enhances the search functionality by supporting multiple criteria.
73-73
: Method call aligns with the updated service method
The call to animalService.searchAnimals(searchCriteria, pageSize, scrollId)
correctly passes the updated parameters, ensuring consistency between the controller and service layers.
jpa/keyset-pagination/boot-data-window-pagination/src/main/java/com/example/keysetpagination/repositories/CustomRepositoryImpl.java (1)
85-85
: Ensure that 'value' implements Comparable to prevent ClassCastException
Casting value
to (Comparable)
assumes that all values are Comparable
. Ensure that the values in keysetMap
implement Comparable
to avoid runtime exceptions.
Also applies to: 87-87
jpa/keyset-pagination/boot-data-window-pagination/src/main/java/com/example/keysetpagination/services/EntitySpecification.java (1)
48-80
: Ensure Compatibility with Target Java Version
The use of the enhanced switch
expression with arrow labels and the yield
statement requires Java 14 or higher. If the project aims to support earlier Java versions, this code will cause compatibility issues.
Run the following script to check the Java version specified in the project's pom.xml
:
Ensure that the maven.compiler.source
and maven.compiler.target
are set to at least 14
.
✅ Verification successful
Enhanced Switch Expression is Compatible with Project's Java Version
The project uses Java 21 (as specified in pom.xml) and Spring Boot 3.4.0, which both fully support enhanced switch expressions with arrow labels and yield statements. The code's use of these Java features is appropriate and won't cause any compatibility issues.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Check the Java source and target versions in pom.xml
grep -A 1 "<maven.compiler.source>" pom.xml
Length of output: 45
Script:
#!/bin/bash
# Let's try different approaches to find Java version configuration
# 1. Search for java version in parent pom.xml if exists
fd "pom.xml" --exec grep -l "java.version\|source\|target" {}
# 2. Check for Spring Boot parent version which might indicate Java version
fd "pom.xml" --exec grep -A 2 "spring-boot-starter-parent" {}
# 3. Look for any Java version related configuration
fd "pom.xml" --exec grep -A 2 "<java\|<source\|<target" {}
Length of output: 18267
Script:
#!/bin/bash
# Let's specifically check the Java version for the project containing the file under review
fd "pom.xml" --base-directory "jpa/keyset-pagination/boot-data-window-pagination" --exec grep -A 2 "java.version\|spring-boot-starter-parent" {}
Length of output: 504
jpa/keyset-pagination/boot-data-window-pagination/src/main/java/com/example/keysetpagination/services/AnimalService.java (1)
33-36
: Ensure 'EntitySpecification' is properly injected
Verify that EntitySpecification<Animal>
is correctly defined as a Spring Bean (e.g., annotated with @Component
or @Service
) to enable constructor injection. If it's not a Spring-managed bean, this could lead to application context initialization errors.
Run the following script to verify that EntitySpecification
is annotated as a Spring Bean:
✅ Verification successful
EntitySpecification
is properly annotated with @Service
The verification confirms that EntitySpecification
is correctly defined as a Spring-managed bean using the @Service
annotation, which ensures proper dependency injection in the AnimalService
constructor.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Verify if 'EntitySpecification' is annotated with @Component or @Service.
# Search for '@Component' or '@Service' annotations on 'EntitySpecification' class.
rg -A 5 '@(Component|Service)' --type java -g '*EntitySpecification.java'
Length of output: 995
...window-pagination/src/main/java/com/example/keysetpagination/model/query/SearchCriteria.java
Outdated
Show resolved
Hide resolved
...-window-pagination/src/main/java/com/example/keysetpagination/model/query/QueryOperator.java
Outdated
Show resolved
Hide resolved
...-window-pagination/src/main/java/com/example/keysetpagination/model/query/QueryOperator.java
Outdated
Show resolved
Hide resolved
...pagination/src/main/java/com/example/keysetpagination/repositories/CustomRepositoryImpl.java
Show resolved
Hide resolved
...pagination/src/main/java/com/example/keysetpagination/repositories/CustomRepositoryImpl.java
Outdated
Show resolved
Hide resolved
...ndow-pagination/src/main/java/com/example/keysetpagination/services/EntitySpecification.java
Outdated
Show resolved
Hide resolved
...ndow-pagination/src/main/java/com/example/keysetpagination/services/EntitySpecification.java
Outdated
Show resolved
Hide resolved
private void validateMetadata(SearchCriteria[] searchCriteriaArray, Class<T> entityClass) { | ||
Metamodel metamodel = entityManager.getMetamodel(); | ||
ManagedType<T> managedType = metamodel.managedType(entityClass); | ||
|
||
for (SearchCriteria searchCriteria : searchCriteriaArray) { | ||
String fieldName = searchCriteria.getField(); | ||
if (managedType.getAttribute(fieldName) == null) { | ||
throw new IllegalArgumentException("Invalid field: " + fieldName); | ||
} | ||
} | ||
} |
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.
Exception Handling in validateMetadata
Does Not Catch Invalid Fields
The getAttribute
method in ManagedType
throws an IllegalArgumentException
if the attribute does not exist rather than returning null
. Therefore, the check at line 99 will not work as intended.
Modify the code to handle the exception:
-for (SearchCriteria searchCriteria : searchCriteriaArray) {
+for (SearchCriteria searchCriteria : searchCriteriaArray) {
String fieldName = searchCriteria.getField();
- if (managedType.getAttribute(fieldName) == null) {
+ try {
+ managedType.getAttribute(fieldName);
+ } catch (IllegalArgumentException e) {
throw new IllegalArgumentException("Invalid field: " + fieldName);
}
}
📝 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.
private void validateMetadata(SearchCriteria[] searchCriteriaArray, Class<T> entityClass) { | |
Metamodel metamodel = entityManager.getMetamodel(); | |
ManagedType<T> managedType = metamodel.managedType(entityClass); | |
for (SearchCriteria searchCriteria : searchCriteriaArray) { | |
String fieldName = searchCriteria.getField(); | |
if (managedType.getAttribute(fieldName) == null) { | |
throw new IllegalArgumentException("Invalid field: " + fieldName); | |
} | |
} | |
} | |
private void validateMetadata(SearchCriteria[] searchCriteriaArray, Class<T> entityClass) { | |
Metamodel metamodel = entityManager.getMetamodel(); | |
ManagedType<T> managedType = metamodel.managedType(entityClass); | |
for (SearchCriteria searchCriteria : searchCriteriaArray) { | |
String fieldName = searchCriteria.getField(); | |
try { | |
managedType.getAttribute(fieldName); | |
} catch (IllegalArgumentException e) { | |
throw new IllegalArgumentException("Invalid field: " + fieldName); | |
} | |
} | |
} |
...ata-window-pagination/src/main/java/com/example/keysetpagination/services/AnimalService.java
Show resolved
Hide resolved
...ata-window-pagination/src/main/java/com/example/keysetpagination/services/AnimalService.java
Outdated
Show resolved
Hide resolved
...agination/src/test/java/com/example/keysetpagination/web/controllers/AnimalControllerIT.java
Show resolved
Hide resolved
Quality Gate passedIssues Measures |
|
||
@Test | ||
@Disabled("Not Implemented yet") | ||
void shouldBuildSpecificationForANDOperator() { |
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.
@coderabbitai, can you please help me to implement this type in current code , if necessary modify payload
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.
@rajadilipkolli, certainly! Here's how you can implement the shouldBuildSpecificationForANDOperator
test method:
@Test
void shouldBuildSpecificationForANDOperator() {
SearchCriteria criteria1 = new SearchCriteria(QueryOperator.EQ, "type", List.of("Mammal"));
SearchCriteria criteria2 = new SearchCriteria(QueryOperator.EQ, "name", List.of("Lion"));
Specification<Animal> spec = entitySpecification.specificationBuilder(
List.of(criteria1, criteria2),
Animal.class
);
assertThat(spec).isNotNull();
}
Explanation:
-
We create two
SearchCriteria
instances:criteria1
checks if thetype
is equal to"Mammal"
.criteria2
checks if thename
is equal to"Lion"
.
-
By passing both criteria in a list to the
specificationBuilder
method, the method should combine them using a logicalAND
operation. This assumes that yourEntitySpecification
class is designed to combine multiple criteria with logicalAND
by default.
Modifying the Payload (if necessary):
If your specificationBuilder
method requires an explicit operator to combine the criteria, you might need to adjust your SearchCriteria
class and payload to include a logical operator field. Here's how you can modify the SearchCriteria
class:
public class SearchCriteria {
private QueryOperator queryOperator;
private String field;
private List<String> values;
private LogicalOperator logicalOperator; // NEW FIELD
// Getters, setters, constructors...
}
Define an enum for LogicalOperator
:
public enum LogicalOperator {
AND,
OR
}
Adjust your test method accordingly:
@Test
void shouldBuildSpecificationForANDOperator() {
SearchCriteria criteria1 = new SearchCriteria(QueryOperator.EQ, "type", List.of("Mammal"), LogicalOperator.AND);
SearchCriteria criteria2 = new SearchCriteria(QueryOperator.EQ, "name", List.of("Lion"), null);
Specification<Animal> spec = entitySpecification.specificationBuilder(
List.of(criteria1, criteria2),
Animal.class
);
assertThat(spec).isNotNull();
}
Updates to EntitySpecification
Class:
Ensure that your EntitySpecification
class handles the logical operators correctly when combining predicates. Modify the specificationBuilder
method to check for the LogicalOperator
in each SearchCriteria
and combine predicates accordingly.
Feel free to let me know if you need further assistance with modifying the implementation!
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.
@coderabbitai, above change cant handle a scenario where (name = 'someName' and (type ='someType' or habbitant = 'someHabbitant'))
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.
@rajadilipkolli, thank you for bringing this up. To handle scenarios like (name = 'someName' AND (type ='someType' OR habitat = 'someHabitat'))
, we'll need to enhance our implementation to support nested logical operators and grouping of criteria.
Here's how you can achieve this:
1. Introduce a CriteriaGroup
Class
Create a new class to represent groups of criteria, allowing for nested logical combinations.
public class CriteriaGroup {
private LogicalOperator logicalOperator;
private List<Object> criteria; // Can be SearchCriteria or CriteriaGroup
public CriteriaGroup(LogicalOperator logicalOperator, List<Object> criteria) {
this.logicalOperator = logicalOperator;
this.criteria = criteria;
}
// Getters and setters
}
2. Update the LogicalOperator
Enum
Ensure you have an enum to specify logical operators.
public enum LogicalOperator {
AND,
OR
}
3. Modify the EntitySpecification
Class
Update your EntitySpecification
to handle CriteriaGroup
recursively.
public Specification<T> specificationBuilder(Object criteriaObject, Class<T> entityClass) {
return (root, query, criteriaBuilder) -> buildPredicate(criteriaObject, root, criteriaBuilder);
}
private Predicate buildPredicate(Object criteriaObject, Root<T> root, CriteriaBuilder criteriaBuilder) {
if (criteriaObject instanceof SearchCriteria) {
SearchCriteria searchCriteria = (SearchCriteria) criteriaObject;
return createPredicate(searchCriteria, root, criteriaBuilder);
} else if (criteriaObject instanceof CriteriaGroup) {
CriteriaGroup criteriaGroup = (CriteriaGroup) criteriaObject;
List<Predicate> predicates = new ArrayList<>();
for (Object obj : criteriaGroup.getCriteria()) {
predicates.add(buildPredicate(obj, root, criteriaBuilder));
}
if (criteriaGroup.getLogicalOperator() == LogicalOperator.AND) {
return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
} else {
return criteriaBuilder.or(predicates.toArray(new Predicate[0]));
}
} else {
throw new IllegalArgumentException("Unknown criteria object");
}
}
4. Update the Test Method
Here's how you can write the test to handle nested criteria:
@Test
void shouldBuildSpecificationForNestedCriteria() {
SearchCriteria nameCriteria = new SearchCriteria(QueryOperator.EQ, "name", List.of("someName"));
SearchCriteria typeCriteria = new SearchCriteria(QueryOperator.EQ, "type", List.of("someType"));
SearchCriteria habitatCriteria = new SearchCriteria(QueryOperator.EQ, "habitat", List.of("someHabitat"));
CriteriaGroup innerGroup = new CriteriaGroup(LogicalOperator.OR, List.of(typeCriteria, habitatCriteria));
CriteriaGroup outerGroup = new CriteriaGroup(LogicalOperator.AND, List.of(nameCriteria, innerGroup));
Specification<Animal> spec = entitySpecification.specificationBuilder(outerGroup, Animal.class);
assertThat(spec).isNotNull();
}
5. Adjust the SearchCriteria
Class (Optional)
Since logical operators are now handled in CriteriaGroup
, you can remove any logical operator fields from SearchCriteria
if present.
public class SearchCriteria {
private QueryOperator queryOperator;
private String field;
private List<String> values;
// Constructors, getters, and setters
}
6. Modify the Controller (If Necessary)
If your API needs to accept nested criteria, adjust the controller to handle CriteriaGroup
:
@PostMapping("/search")
public Window<AnimalResponse> searchAnimals(@RequestBody @Valid CriteriaGroup criteriaGroup,
@RequestParam int pageSize,
@RequestParam(required = false) Long scrollId) {
Window<AnimalResponse> animals = animalService.searchAnimals(criteriaGroup, pageSize, scrollId);
if (animals.content().isEmpty()) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "No animals found matching criteria");
}
return animals;
}
7. Update the Service Method
Modify your service method to accept CriteriaGroup
:
public Window<AnimalResponse> searchAnimals(CriteriaGroup criteriaGroup, int pageSize, Long scrollId) {
Specification<Animal> specification = entitySpecification.specificationBuilder(criteriaGroup, Animal.class);
// Rest of the method remains the same
}
Summary
By introducing the CriteriaGroup
, you enable the construction of complex, nested logical queries, allowing you to handle cases like:
(name = 'someName' AND (type ='someType' OR habitat = 'someHabitat'))
This approach provides flexibility and scalability for your querying needs.
Let me know if you need further assistance with implementing these changes!
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.
Actionable comments posted: 8
🧹 Outside diff range and nitpick comments (11)
jpa/keyset-pagination/boot-data-window-pagination/src/main/java/com/example/keysetpagination/web/controllers/AnimalController.java (1)
61-62
: Consider adding request body documentationThe API response documentation is well-defined. However, consider adding
@Parameter
or@Schema
documentation for thesearchCriteria
request body to improve API documentation completeness.Example addition:
@Parameter(description = "Search criteria for filtering animals", example = "[{\"field\": \"name\", \"queryOperator\": \"EQUALS\", \"values\": [\"Leo\"]}]") @RequestBody @Valid List<SearchCriteria> searchCriteriajpa/keyset-pagination/boot-data-window-pagination/src/main/java/com/example/keysetpagination/repositories/CustomRepository.java (2)
19-19
: Fix the typo in Javadoc parameter description.There's a misspelling: "entites" should be "entities".
Apply this diff to correct the typo:
- * @param spec The specification to filter entites + * @param spec The specification to filter entities
26-26
: Consider simplifying method signature by removingClass<T> entityClass
parameter.If feasible, refactor the method to eliminate the need for the
entityClass
parameter, as the generic typeT
may suffice for operation.jpa/keyset-pagination/boot-data-window-pagination/src/test/java/com/example/keysetpagination/services/EntitySpecificationIntTest.java (2)
29-136
: Add negative test cases for error scenariosThe test suite lacks negative test cases. Consider adding tests for:
- Invalid field names
- Incompatible value types
- Empty or null criteria lists
- Invalid operator for field type
Example:
@Test void shouldThrowExceptionForInvalidFieldName() { SearchCriteria criteria = new SearchCriteria(QueryOperator.EQ, "invalidField", List.of("value")); assertThatThrownBy(() -> entitySpecification.specificationBuilder(List.of(criteria), Animal.class)) .isInstanceOf(IllegalArgumentException.class) .hasMessageContaining("Invalid field name"); }
1-137
: Consider performance implications of dynamic specificationsWhile the implementation provides flexible querying capabilities, be mindful of performance implications:
- Complex specifications with multiple conditions might generate suboptimal SQL
- Consider adding test cases with larger datasets to verify performance
- Monitor query execution plans for different combinations of operators
Consider implementing:
- Query result caching for common search patterns
- Limit clause for large result sets
- Index hints for frequently used search fields
jpa/keyset-pagination/boot-data-window-pagination/src/test/java/com/example/keysetpagination/web/controllers/AnimalControllerIT.java (3)
Line range hint
79-125
: Consider extracting repeated JSON content to a constant.The same JSON search criteria is used in both requests. Consider extracting it to a constant to improve maintainability and reduce duplication.
+ private static final String BIRD_TYPE_SEARCH_CRITERIA = """ + [ + { + "queryOperator": "EQ", + "field": "type", + "values": [ + "Bird" + ] + } + ] + """;
132-154
: Improve assertion documentation.The test would be clearer with a more descriptive comment explaining the expected result calculation.
- .andExpect(jsonPath("$.content", hasSize(4))) + // Total animals (10) - Mammals (3) - Birds (3) = 4 animals + .andExpect(jsonPath("$.content", hasSize(4)))
159-173
: Enhance test coverage for non-existent values.Consider expanding the test to:
- Test with multiple non-existent values
- Add assertions to verify the structure of empty content
.andExpect(status().isOk()) .andExpect(jsonPath("$.content", hasSize(0))) + .andExpect(jsonPath("$.content").isArray()) + .andExpect(jsonPath("$.totalElements", is(0))) .andExpect(jsonPath("$.last", is(true)));jpa/keyset-pagination/boot-data-window-pagination/src/main/java/com/example/keysetpagination/services/EntitySpecification.java (3)
78-85
: Add case-insensitive string comparison option.For string fields, consider adding case-insensitive versions of LIKE, CONTAINS, STARTS_WITH, and ENDS_WITH operators.
case LIKE, CONTAINS -> combinePredicates( typedValues, - value -> criteriaBuilder.like(root.get(fieldName), "%" + value + "%"), + value -> criteriaBuilder.like(criteriaBuilder.lower(root.get(fieldName)), + "%" + value.toString().toLowerCase() + "%"), criteriaBuilder::or);
101-126
: Enhance type conversion support.Consider adding support for additional common types and custom date formats:
private Object convertToType(String value, Class<?> fieldType) { try { if (fieldType.equals(String.class)) { return value; + } else if (fieldType.equals(BigDecimal.class)) { + return new BigDecimal(value); + } else if (fieldType.equals(UUID.class)) { + return UUID.fromString(value); } else if (fieldType.equals(LocalDate.class)) { - return LocalDate.parse(value); + return LocalDate.parse(value, DateTimeFormatter.ISO_DATE); } else if (fieldType.equals(LocalDateTime.class)) { - return LocalDateTime.parse(value); + return LocalDateTime.parse(value, DateTimeFormatter.ISO_DATE_TIME);
128-136
: Improve error handling and optimize for single value case.The error message could be more specific, and the method could be optimized for single value cases.
private Predicate combinePredicates( List<Object> values, Function<Object, Predicate> predicateFunction, BiFunction<Predicate, Predicate, Predicate> combiner) { + if (values.size() == 1) { + return predicateFunction.apply(values.get(0)); + } return values.stream() .map(predicateFunction) .reduce(combiner::apply) - .orElseThrow(() -> new IllegalArgumentException("No predicates could be generated from values")); + .orElseThrow(() -> new IllegalArgumentException( + String.format("No predicates could be generated from values: %s", values))); }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (9)
jpa/keyset-pagination/boot-data-window-pagination/src/main/java/com/example/keysetpagination/model/query/QueryOperator.java
(1 hunks)jpa/keyset-pagination/boot-data-window-pagination/src/main/java/com/example/keysetpagination/model/query/SearchCriteria.java
(1 hunks)jpa/keyset-pagination/boot-data-window-pagination/src/main/java/com/example/keysetpagination/repositories/CustomRepository.java
(1 hunks)jpa/keyset-pagination/boot-data-window-pagination/src/main/java/com/example/keysetpagination/repositories/CustomRepositoryImpl.java
(1 hunks)jpa/keyset-pagination/boot-data-window-pagination/src/main/java/com/example/keysetpagination/services/AnimalService.java
(3 hunks)jpa/keyset-pagination/boot-data-window-pagination/src/main/java/com/example/keysetpagination/services/EntitySpecification.java
(1 hunks)jpa/keyset-pagination/boot-data-window-pagination/src/main/java/com/example/keysetpagination/web/controllers/AnimalController.java
(3 hunks)jpa/keyset-pagination/boot-data-window-pagination/src/test/java/com/example/keysetpagination/services/EntitySpecificationIntTest.java
(1 hunks)jpa/keyset-pagination/boot-data-window-pagination/src/test/java/com/example/keysetpagination/web/controllers/AnimalControllerIT.java
(3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- jpa/keyset-pagination/boot-data-window-pagination/src/main/java/com/example/keysetpagination/repositories/CustomRepositoryImpl.java
👮 Files not reviewed due to content moderation or server errors (3)
- jpa/keyset-pagination/boot-data-window-pagination/src/main/java/com/example/keysetpagination/model/query/SearchCriteria.java
- jpa/keyset-pagination/boot-data-window-pagination/src/main/java/com/example/keysetpagination/model/query/QueryOperator.java
- jpa/keyset-pagination/boot-data-window-pagination/src/main/java/com/example/keysetpagination/services/AnimalService.java
🔇 Additional comments (5)
jpa/keyset-pagination/boot-data-window-pagination/src/main/java/com/example/keysetpagination/web/controllers/AnimalController.java (2)
5-5
: LGTM: Import statements are appropriate
The new imports for SearchCriteria
and List
are necessary for the enhanced search functionality.
Also applies to: 19-19
64-74
: LGTM: Well-structured endpoint with appropriate HTTP method
The change from GET to POST is appropriate for complex search criteria, and the implementation properly handles validation and pagination.
Verify scrollId implementation
Let's verify the scrollId implementation to ensure proper cursor-based pagination handling.
✅ Verification successful
Let me gather more context about the implementation by checking the repository layer and the entity definition.
scrollId implementation is properly handled
The implementation shows correct cursor-based pagination handling:
- Proper null handling in service layer using ternary operator
- Consistent usage of ScrollPosition for keyset-based pagination
- Custom repository implementation properly handles the scrollId through KeysetScrollPosition
- Correct forward direction maintained for cursor movement
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Check for proper scrollId handling in the service layer
ast-grep --pattern 'searchAnimals($$$scrollId$$$) {
$$$
}'
# Check for potential null handling of scrollId
rg -A 5 "scrollId.*null|Optional.*scrollId"
Length of output: 1099
Script:
#!/bin/bash
# Check AnimalRepository implementation
ast-grep --pattern 'interface AnimalRepository {
$$$
}'
# Check Animal entity definition for id field
ast-grep --pattern 'class Animal {
$$$
id
$$$
}'
# Check if there are any other usages of scrollId
rg -A 3 "ScrollPosition.of|ScrollPosition.keyset"
Length of output: 2899
jpa/keyset-pagination/boot-data-window-pagination/src/main/java/com/example/keysetpagination/repositories/CustomRepository.java (1)
14-27
: Interface design is clean and promotes code reusability.
The CustomRepository<T>
interface is well-defined, facilitating flexible keyset pagination for various entity types.
jpa/keyset-pagination/boot-data-window-pagination/src/main/java/com/example/keysetpagination/services/EntitySpecification.java (2)
1-28
: LGTM! Well-structured service class with proper dependency injection.
The class is well-designed with a generic type parameter allowing reuse across different entities. Constructor injection is used appropriately for the EntityManager dependency.
138-148
:
Fix metadata validation and consider caching.
The current implementation has two issues:
- Incorrect handling of non-existent attributes (duplicate of previous review)
- Performance could be improved by caching metamodel information
Consider adding metamodel caching:
@Service
public class EntitySpecification<T> {
+ private final Map<Class<?>, ManagedType<?>> metamodelCache = new ConcurrentHashMap<>();
private final EntityManager entityManager;
private void validateMetadata(List<SearchCriteria> searchCriteriaList, Class<T> entityClass) {
- Metamodel metamodel = entityManager.getMetamodel();
- ManagedType<T> managedType = metamodel.managedType(entityClass);
+ ManagedType<T> managedType = (ManagedType<T>) metamodelCache.computeIfAbsent(
+ entityClass,
+ key -> entityManager.getMetamodel().managedType(key));
@Test | ||
void shouldBuildSpecificationForEQOperator() { | ||
SearchCriteria criteria = new SearchCriteria(QueryOperator.EQ, "type", List.of("Mammal")); | ||
Specification<Animal> spec = entitySpecification.specificationBuilder(List.of(criteria), Animal.class); | ||
assertThat(spec).isNotNull(); | ||
} |
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.
🛠️ Refactor suggestion
Enhance test coverage with actual query execution and assertions
The current tests only verify that specifications are created (not null). This doesn't guarantee that the specifications generate the expected SQL queries or return the correct results.
Example enhancement for one test:
@Test
void shouldBuildSpecificationForEQOperator() {
SearchCriteria criteria = new SearchCriteria(QueryOperator.EQ, "type", List.of("Mammal"));
Specification<Animal> spec = entitySpecification.specificationBuilder(List.of(criteria), Animal.class);
- assertThat(spec).isNotNull();
+ List<Animal> results = animalRepository.findAll(spec);
+ assertThat(results)
+ .isNotEmpty()
+ .extracting("type")
+ .containsOnly("Mammal");
}
Also applies to: 36-41, 43-48, 50-55, 57-62, 64-69, 71-76, 78-83, 85-90, 92-97, 99-104, 106-111, 113-118, 131-136
@Test | ||
@Disabled("Not Implemented yet") | ||
void shouldBuildSpecificationForANDOperator() { | ||
SearchCriteria criteria1 = new SearchCriteria(QueryOperator.EQ, "type", List.of("Bird")); | ||
SearchCriteria criteria2 = new SearchCriteria(QueryOperator.EQ, "habitat", List.of("Forest")); | ||
SearchCriteria criteriaAnd = new SearchCriteria(QueryOperator.AND, null, null); | ||
Specification<Animal> spec = | ||
entitySpecification.specificationBuilder(List.of(criteria1, criteriaAnd, criteria2), Animal.class); | ||
assertThat(spec).isNotNull(); | ||
} |
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.
Implement AND operator and update test
The AND operator test is currently disabled. This indicates missing functionality that should be implemented.
Would you like me to help implement the AND operator in the EntitySpecification
class?
...gination/src/test/java/com/example/keysetpagination/services/EntitySpecificationIntTest.java
Show resolved
Hide resolved
...agination/src/test/java/com/example/keysetpagination/web/controllers/AnimalControllerIT.java
Show resolved
Hide resolved
] | ||
""")) | ||
.andExpect(status().isOk()) | ||
.andExpect(jsonPath("$.content.size()", is(4))) // "Parrot", "Penguin", "Shark", "Eagle" |
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.
Fix incorrect assertion comment.
The comment lists incorrect number of animals matching the Bird and Fish types.
- .andExpect(jsonPath("$.content.size()", is(4))) // "Parrot", "Penguin", "Shark", "Eagle"
+ .andExpect(jsonPath("$.content.size()", is(4))) // Birds ("Parrot", "Penguin", "Eagle") + Fish ("Shark")
📝 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.
.andExpect(jsonPath("$.content.size()", is(4))) // "Parrot", "Penguin", "Shark", "Eagle" | |
.andExpect(jsonPath("$.content.size()", is(4))) // Birds ("Parrot", "Penguin", "Eagle") + Fish ("Shark") |
{ | ||
"queryOperator": "OR", | ||
"field": "name", | ||
"values": ["Shark", "Eagle"] | ||
} |
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.
Fix incorrect OR operator implementation.
The OR operator implementation differs from the pattern used in other tests. It should use separate criteria with an OR operator between them.
- {
- "queryOperator": "OR",
- "field": "name",
- "values": ["Shark", "Eagle"]
- }
+ {
+ "queryOperator": "EQ",
+ "field": "name",
+ "values": ["Shark"]
+ },
+ {
+ "queryOperator": "OR",
+ "field": null,
+ "values": null
+ },
+ {
+ "queryOperator": "EQ",
+ "field": "name",
+ "values": ["Eagle"]
+ }
📝 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.
{ | |
"queryOperator": "OR", | |
"field": "name", | |
"values": ["Shark", "Eagle"] | |
} | |
{ | |
"queryOperator": "EQ", | |
"field": "name", | |
"values": ["Shark"] | |
}, | |
{ | |
"queryOperator": "OR", | |
"field": null, | |
"values": null | |
}, | |
{ | |
"queryOperator": "EQ", | |
"field": "name", | |
"values": ["Eagle"] | |
} |
case OR -> criteriaBuilder.or(root.get(fieldName).in(typedValues)); | ||
case AND -> criteriaBuilder.and(root.get(fieldName).in(typedValues)); |
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.
🛠️ Refactor suggestion
Reconsider OR and AND operators with IN clause.
The OR and AND operators combined with IN clause might be confusing and potentially redundant. Consider removing these operators as they might lead to unexpected behavior.
- case OR -> criteriaBuilder.or(root.get(fieldName).in(typedValues));
- case AND -> criteriaBuilder.and(root.get(fieldName).in(typedValues));
Summary by CodeRabbit
Release Notes
New Features
SearchCriteria
for querying animals.QueryOperator
enum.Improvements
AnimalController
to accept search criteria via aPOST
request.AnimalService
to utilize specifications for dynamic query generation.CustomRepository
interface for improved pagination support.Bug Fixes
Refactor
WindowPaginationApplication
for clarity.