diff --git a/api/src/main/java/jakarta/data/Limit.java b/api/src/main/java/jakarta/data/Limit.java index 1617284fe..5a18cb875 100644 --- a/api/src/main/java/jakarta/data/Limit.java +++ b/api/src/main/java/jakarta/data/Limit.java @@ -34,7 +34,7 @@ * *
* @Find - * Product[] namedLike(@By(_Product.NAME) @Is(Like.class) String namePattern, + * Product[] namedLike(@By(_Product.NAME) @IgnoreCase @Is(Like.class) String namePattern, * Limit limit, * Sort<?>... sorts); * diff --git a/api/src/main/java/jakarta/data/constraint/Constraint.java b/api/src/main/java/jakarta/data/constraint/Constraint.java index c038d6cb6..d31101e11 100644 --- a/api/src/main/java/jakarta/data/constraint/Constraint.java +++ b/api/src/main/java/jakarta/data/constraint/Constraint.java @@ -44,8 +44,8 @@ ** @Find * List<Car> withinYears(@By(_Car.YEAR) Between<Integer> year, - * @By(_Car.MAKE) Like makePattern, - * @By(_Car.MODEL) Like modelPattern, + * @By(_Car.MAKE) @IgnoreCase Like makePattern, + * @By(_Car.MODEL) @IgnoreCase Like modelPattern, * Order<Car> sorts); * * ... @@ -68,8 +68,8 @@ ** @Find * List<Car> pricedAtMost(@By(_Car.PRICE) @Is(AtMost.class) int maxPrice, - * @By(_Car.MAKE) @Is(Like.class) String makePattern, - * @By(_Car.MODEL) @Is(Like.class) Sting modelPattern, + * @By(_Car.MAKE) @IgnoreCase @Is(Like.class) String makePattern, + * @By(_Car.MODEL) @IgnoreCase @Is(Like.class) Sting modelPattern, * Order<Car> sorts); * * ... diff --git a/api/src/main/java/jakarta/data/constraint/EqualTo.java b/api/src/main/java/jakarta/data/constraint/EqualTo.java index e01c2ab66..60bbbbf62 100644 --- a/api/src/main/java/jakarta/data/constraint/EqualTo.java +++ b/api/src/main/java/jakarta/data/constraint/EqualTo.java @@ -36,16 +36,16 @@ * ** @Find - * List<Car> fromManufacturer(@By(_Car.MAKE) EqualTo<String> manufacturer); + * List<Car> fromManufacturer(@By(_Car.MAKE) @IgnoreCase EqualTo<String> manufacturer); * * @Find - * List<Car> ofMakeAndModel(@By(_Car.MAKE) @Is(EqualTo.class) String manufacturer, - * @By(_Car.MODEL) @Is(EqualTo.class) String model, + * List<Car> ofMakeAndModel(@By(_Car.MAKE) @IgnoreCase @Is(EqualTo.class) String manufacturer, + * @By(_Car.MODEL) @IgnoreCase @Is(EqualTo.class) String model, * Order<Car> sorts); * * @Find - * List<Car> ofMakeAndModelAndYear(@By(_Car.MAKE) String manufacturer, - * @By(_Car.MODEL) String model, + * List<Car> ofMakeAndModelAndYear(@By(_Car.MAKE) @IgnoreCase String manufacturer, + * @By(_Car.MODEL) @IgnoreCase String model, * @By(_Car.YEAR) int modelYear, * Order<Car> sorts); * ... diff --git a/api/src/main/java/jakarta/data/constraint/Like.java b/api/src/main/java/jakarta/data/constraint/Like.java index 76e1372fc..0c8b0d488 100644 --- a/api/src/main/java/jakarta/data/constraint/Like.java +++ b/api/src/main/java/jakarta/data/constraint/Like.java @@ -41,11 +41,12 @@ * List<Car> matchVIN(@By(_Car.VIN) Like vinPattern); * * @Find // requires the -parameters compiler option to preserve parameter names - * List<Car> makeAndModel(Like make, Like model); + * List<Car> makeAndModel(@IgnoreCase Like make, + * @IgnoreCase Like model); * * @Find - * List<Car> search(@By(_Car.MAKE) @Is(Like.class) String makePattern, - * @By(_Car.MODEL) @Is(Like.class) String modelPattern, + * List<Car> search(@By(_Car.MAKE) @IgnoreCase @Is(Like.class) String makePattern, + * @By(_Car.MODEL) @IgnoreCase @Is(Like.class) String modelPattern, * Order<Car> sorts); * * ... diff --git a/api/src/main/java/jakarta/data/constraint/NotEqualTo.java b/api/src/main/java/jakarta/data/constraint/NotEqualTo.java index e8e81980d..30da7777c 100644 --- a/api/src/main/java/jakarta/data/constraint/NotEqualTo.java +++ b/api/src/main/java/jakarta/data/constraint/NotEqualTo.java @@ -37,8 +37,8 @@ * List<Car> excludingManufacturer(@By(_Car.MAKE) NotEqualTo<String> excludedManufacturer); * * @Find - * List<Car> ofMakeNotModel(@By(_Car.MAKE) @Is(EqualTo.class) String manufacturer, - * @By(_Car.MODEL) @Is(NotEqualTo.class) String excludedModel, + * List<Car> ofMakeNotModel(@By(_Car.MAKE) @IgnoreCase @Is(EqualTo.class) String manufacturer, + * @By(_Car.MODEL) @IgnoreCase @Is(NotEqualTo.class) String excludedModel, * Order<Car> sorts); * ... * diff --git a/api/src/main/java/jakarta/data/constraint/NotLike.java b/api/src/main/java/jakarta/data/constraint/NotLike.java index 134187eb4..c022ab6a4 100644 --- a/api/src/main/java/jakarta/data/constraint/NotLike.java +++ b/api/src/main/java/jakarta/data/constraint/NotLike.java @@ -38,11 +38,11 @@ * *** @Find - * List<Car> matchVIN(@By(_Car.VIN) NotLike pattern); + * List<Car> matchVIN(@By(_Car.VIN) @IgnoreCase NotLike pattern); * * @Find - * List<Car> ofMakeNotModel(@By(_Car.MAKE) String manufacturer, - * @By(_Car.MODEL) @Is(NotLike.class) String excludePattern, + * List<Car> ofMakeNotModel(@By(_Car.MAKE) @IgnoreCase String manufacturer, + * @By(_Car.MODEL) @IgnoreCase @Is(NotLike.class) String excludePattern, * Order<Car> sorts); * * ... diff --git a/api/src/main/java/jakarta/data/page/Page.java b/api/src/main/java/jakarta/data/page/Page.java index 03c18aa6f..3964d1208 100644 --- a/api/src/main/java/jakarta/data/page/Page.java +++ b/api/src/main/java/jakarta/data/page/Page.java @@ -30,8 +30,8 @@ * **/ diff --git a/api/src/main/java/jakarta/data/repository/Find.java b/api/src/main/java/jakarta/data/repository/Find.java index d664f7c4b..33320c767 100644 --- a/api/src/main/java/jakarta/data/repository/Find.java +++ b/api/src/main/java/jakarta/data/repository/Find.java @@ -79,7 +79,7 @@ * @Repository * interface Garage { * @Find - * List<Car> getCarsWithModel(@By("model") String model); + * List<Car> getCarsWithModel(@By("model") @IgnoreCase String model); * } ** @Find - * Page<Vehicle> search(@By("make") String make, - * @By("model") String model, + * Page<Vehicle> search(@By("make") @IgnoreCase String make, + * @By("model") @IgnoreCase String model, * @By("year") int designYear, * PageRequest pageRequest, * Order<Vehicle> order); diff --git a/api/src/main/java/jakarta/data/repository/By.java b/api/src/main/java/jakarta/data/repository/By.java index 5472e1622..e951b23ac 100644 --- a/api/src/main/java/jakarta/data/repository/By.java +++ b/api/src/main/java/jakarta/data/repository/By.java @@ -52,11 +52,11 @@ * Person findById(@By(ID) String id); // maps to Person.ssn * * @Find - * List<Person> findNamed(@By("firstName") String first, - * @By("lastName") String last); + * List<Person> findNamed(@By("firstName") @IgnoreCase String first, + * @By("lastName") @IgnoreCase String last); * * @Find - * List<Person> findByCity(@By("address.city") String city); + * List<Person> findByCity(@By("address.city") @IgnoreCase String city); * } ** @@ -76,11 +76,11 @@ * Person findById(String ssn); * * @Find - * List<Person> findNamed(String firstName, - * String lastname); + * List<Person> findNamed(@IgnoreCase String firstName, + * @IgnoreCase String lastname); * * @Find - * List<Person> findByCity(String address_city); + * List<Person> findByCity(@IgnoreCase String address_city); * } *The {@code @Find} annotation indicates that the {@code getCarsWithModel(model)} diff --git a/api/src/main/java/jakarta/data/repository/IgnoreCase.java b/api/src/main/java/jakarta/data/repository/IgnoreCase.java new file mode 100644 index 000000000..352cd4261 --- /dev/null +++ b/api/src/main/java/jakarta/data/repository/IgnoreCase.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2025 Contributors to the Eclipse Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package jakarta.data.repository; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import jakarta.data.constraint.AtLeast; +import jakarta.data.constraint.AtMost; +import jakarta.data.constraint.Between; +import jakarta.data.constraint.EqualTo; +import jakarta.data.constraint.GreaterThan; +import jakarta.data.constraint.LessThan; +import jakarta.data.constraint.Like; +import jakarta.data.constraint.NotBetween; +import jakarta.data.constraint.NotEqualTo; +import jakarta.data.constraint.NotLike; + +/** + *
Annotates a parameter of a repository {@link Find} or {@link Delete} + * method, to compare an entity attribute ignoring case.
+ * + *The parameter must correspond to a {@link String}-typed entity attribute + * and must either be an implicit equality constraint or one of the following + * constraints, indicated by the method parameter's type or its {@link Is @Is} + * annotation. + * + *
For example,
+ * + *
+ * @Repository
+ * public interface People extends BasicRepository<Person, Long> {
+ *
+ * // List of Person entities where the lastName attribute matches a
+ * // literal value, ignoring case.
+ * // Requires the -parameters compile option.
+ * @Find
+ * List<Person> ofSurname(@IgnoreCase String lastName);
+ *
+ * // List of Person entities where the lastName attribute matches a
+ * // pattern, ignoring case.
+ * @Find
+ * @OrderBy(_Person.LASTNAME)
+ * @OrderBy(_Person.FIRSTNAME)
+ * List<Person> surnamed(@By(_Person.LASTNAME) @IgnoreCase Like pattern);
+ *
+ * // List of Person entities where the lastName attribute, is alphabetized
+ * // within the first parameter up to but not including the second
+ * // parameter, ignoring case.
+ * @Find
+ * @OrderBy(_Person.LASTNAME)
+ * @OrderBy(_Person.FIRSTNAME)
+ * List<Person> withinSurnameRange(
+ * @By(_Person.LASTNAME) @IgnoreCase @Is(AtLeast.class) String beginAt,
+ * @By(_Person.LASTNAME) @IgnoreCase @Is(LessThan.class) String endBefore);
+ * }
+ *
+ * ...
+ *
+ * smiths = people.ofSurname("smith");
+ * aNames = people.surnamed(Like.pattern("A%"));
+ * surnamesAToE = people.withinSurnameRange("A", "F");
+ *
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.PARAMETER)
+public @interface IgnoreCase {
+}
diff --git a/api/src/main/java/jakarta/data/repository/Is.java b/api/src/main/java/jakarta/data/repository/Is.java
index 3d8abea58..74d851422 100644
--- a/api/src/main/java/jakarta/data/repository/Is.java
+++ b/api/src/main/java/jakarta/data/repository/Is.java
@@ -59,7 +59,7 @@
*
* // Find a page of Product entities where the name field matches a pattern.
* @Find
- * Page<Product> search(@By(_Product.NAME) @Is(Like.class) String pattern,
+ * Page<Product> search(@By(_Product.NAME) @IgnoreCase @Is(Like.class) String pattern,
* PageRequest pagination,
* Order<Product> order);
*
diff --git a/api/src/main/java/jakarta/data/repository/OrderBy.java b/api/src/main/java/jakarta/data/repository/OrderBy.java
index a6458e4d9..e419b8052 100644
--- a/api/src/main/java/jakarta/data/repository/OrderBy.java
+++ b/api/src/main/java/jakarta/data/repository/OrderBy.java
@@ -119,7 +119,7 @@
*
* @Find
* @OrderBy("age")
- * Stream<Person> withLastName(@By("lastName") String surname);
+ * Stream<Person> withLastName(@By("lastName") @IgnoreCase String surname);
*
*
* @return entity attribute name.
diff --git a/api/src/main/java/jakarta/data/repository/Repository.java b/api/src/main/java/jakarta/data/repository/Repository.java
index 1c68fc539..282b47364 100644
--- a/api/src/main/java/jakarta/data/repository/Repository.java
+++ b/api/src/main/java/jakarta/data/repository/Repository.java
@@ -40,7 +40,7 @@
*
* @Find
* @OrderBy("price")
- * List<Product> namedLike(@By("name") @Is(Like.class) String namePattern);
+ * List<Product> namedLike(@By("name") @IgnoreCase @Is(Like.class) String namePattern);
*
* @Query("UPDATE Product SET price = price - (price * ?1) WHERE price * ?1 <= ?2")
* int putOnSale(float rateOfDiscount, float maxDiscount);
diff --git a/api/src/main/java/jakarta/data/restrict/Restriction.java b/api/src/main/java/jakarta/data/restrict/Restriction.java
index 255ce9276..64978d02b 100644
--- a/api/src/main/java/jakarta/data/restrict/Restriction.java
+++ b/api/src/main/java/jakarta/data/restrict/Restriction.java
@@ -46,8 +46,8 @@
* public interface Cars extends CrudRepository<Car, String> {
*
* @Find
- * List<Car> search(@By(_Car.MAKE) String manufacturer,
- * @By(_Car.MODEL) String model,
+ * List<Car> search(@By(_Car.MAKE) @IgnoreCase String manufacturer,
+ * @By(_Car.MODEL) @IgnoreCase String model,
* Restriction<Car> restriction,
* Order<Car>... sort);
* }
diff --git a/api/src/main/java/module-info.java b/api/src/main/java/module-info.java
index 021de152f..0e750b0d1 100644
--- a/api/src/main/java/module-info.java
+++ b/api/src/main/java/module-info.java
@@ -86,7 +86,7 @@
*
* @Find
* List<Product> search(
- * @By(_Product.NAME) @Is(Like.class) String namePattern,
+ * @By(_Product.NAME) @IgnoreCase @Is(Like.class) String namePattern,
* Restriction<Product> restriction,
* Order<Product> sortBy);
*
@@ -383,7 +383,7 @@
*
* @Find
* @OrderBy("price")
- * Product[] named(String name);
+ * Product[] named(@IgnoreCase String name);
*
*
* * @Find - * Stream<Person> livingInCity(String address_city); + * Stream<Person> livingInCity(@IgnoreCase String address_city); ** *
* @Find
- * Page<Product> pricedWithin(@By("name") @Is(Like.class) String pattern,
+ * Page<Product> pricedWithin(@By("name") @IgnoreCase @Is(Like.class) String pattern,
* @By("price") @Is(AtLeast.class) float minPrice,
* @By("price") @Is(AtMost.class) float maxPrice,
* PageRequest pageRequest,
@@ -917,7 +917,7 @@
*
*
* @Find
- * Product[] named(@By("name") @Is(Like.class) String pattern,
+ * Product[] named(@By("name") @IgnoreCase @Is(Like.class) String pattern,
* Limit max,
* Order<Product> sortBy);
*
@@ -935,7 +935,7 @@
*
*
* @Find
- * Product[] namedLike(@By("name") @Is(Like.class) String pattern,
+ * Product[] namedLike(@By("name") @IgnoreCase @Is(Like.class) String pattern,
* Limit max,
* {@code Sort>...} sortBy);
*
@@ -957,7 +957,7 @@
*
*
* @Find
- * List<Product> namedLike(@By(_Product.NAME) Like pattern,
+ * List<Product> namedLike(@By(_Product.NAME) @IgnoreCase Like pattern,
* Restriction<Product> restrict,
* Order<Product> sorts);
*
diff --git a/spec/src/main/asciidoc/repository.asciidoc b/spec/src/main/asciidoc/repository.asciidoc
index e13cb09ac..b443ca91c 100644
--- a/spec/src/main/asciidoc/repository.asciidoc
+++ b/spec/src/main/asciidoc/repository.asciidoc
@@ -412,7 +412,7 @@ For example:
@Repository
public interface ProductRepository {
@Find
- List search(@By(_Product.DESCRIPTION) @Is(Like.class) description,
+ List search(@By(_Product.DESCRIPTION) @IgnoreCase @Is(Like.class) description,
Restriction restriction);
}
----
@@ -760,8 +760,8 @@ In this example, the application uses `OrderBy` to define a subset of the sort c
public interface Products extends CrudRepository {
@Find
@OrderBy(_Car.VEHICLE_CONDITION)
- CursoredPage find(@By(_Car.MAKE) String manufacturer,
- @By(_Car.MODEL) String model,
+ CursoredPage find(@By(_Car.MAKE) @IgnoreCase String manufacturer,
+ @By(_Car.MODEL) @IgnoreCase String model,
PageRequest pageRequest,
Order sorts);
}