From efe4f1ecd920ab4dc11317209b12df0f0f4e261b Mon Sep 17 00:00:00 2001 From: Nathan Rauh <nathan.rauh@us.ibm.com> Date: Tue, 5 Nov 2024 08:21:38 -0600 Subject: [PATCH 1/4] Issue #857 more types of comparisons for parameter based automatic query Signed-off-by: Nathan Rauh <nathan.rauh@us.ibm.com> --- api/src/main/java/jakarta/data/Limit.java | 9 +- .../java/jakarta/data/page/CursoredPage.java | 17 +-- .../java/jakarta/data/page/PageRequest.java | 11 +- .../main/java/jakarta/data/repository/By.java | 5 +- .../main/java/jakarta/data/repository/Is.java | 118 ++++++++++++++++++ .../java/jakarta/data/repository/OrderBy.java | 8 +- .../jakarta/data/repository/Repository.java | 5 +- api/src/main/java/module-info.java | 90 ++++++++----- 8 files changed, 216 insertions(+), 47 deletions(-) create mode 100644 api/src/main/java/jakarta/data/repository/Is.java diff --git a/api/src/main/java/jakarta/data/Limit.java b/api/src/main/java/jakarta/data/Limit.java index ca2948bcd..d06981a2e 100644 --- a/api/src/main/java/jakarta/data/Limit.java +++ b/api/src/main/java/jakarta/data/Limit.java @@ -33,12 +33,15 @@ * For example,</p> * * <pre> - * Product[] findByNameLike(String namePattern, Limit limit, Sort<?>... sorts); + * @Find + * Product[] named(@By(_Product.NAME) @Is(LikeIgnoreCase) String namePattern, + * Limit limit, + * Sort<Product>... sorts); * * ... - * mostExpensive50 = products.findByNameLike(pattern, Limit.of(50), Sort.desc("price")); + * mostExpensive50 = products.named(pattern, Limit.of(50), Sort.desc("price")); * ... - * secondMostExpensive50 = products.findByNameLike(pattern, Limit.range(51, 100), Sort.desc("price")); + * secondMostExpensive50 = products.named(pattern, Limit.range(51, 100), Sort.desc("price")); * </pre> * * <p>A repository method may not be declared with: diff --git a/api/src/main/java/jakarta/data/page/CursoredPage.java b/api/src/main/java/jakarta/data/page/CursoredPage.java index 2ad70d295..71770f8ce 100644 --- a/api/src/main/java/jakarta/data/page/CursoredPage.java +++ b/api/src/main/java/jakarta/data/page/CursoredPage.java @@ -55,23 +55,26 @@ * query parameters) of type {@link PageRequest}, for example:</p> * * <pre> - * @OrderBy("lastName") - * @OrderBy("firstName") - * @OrderBy("id") - * CursoredPage<Employee> findByHoursWorkedGreaterThan(int hours, PageRequest pageRequest); + * @Find + * @OrderBy(_Employee.LASTNAME) + * @OrderBy(_Employee.FIRSTNAME) + * @OrderBy(_Employee.ID) + * CursoredPage<Employee> withOvertime( + * @By(_Employee.HOURSWORKED) @Is(GreaterThan) int fullTimeHours, + * PageRequest pageRequest); * </pre> * * <p>In initial page may be requested using an offset-based page request:</p> * * <pre> - * page = employees.findByHoursWorkedGreaterThan(1500, PageRequest.ofSize(50)); + * page = employees.withOvertime(40, PageRequest.ofSize(50)); * </pre> * * <p>The next page may be requested relative to the end of the current page, * as follows:</p> * * <pre> - * page = employees.findByHoursWorkedGreaterThan(1500, page.nextPageRequest()); + * page = employees.withOvertime(40, page.nextPageRequest()); * </pre> * * <p>Here, the instance of {@link PageRequest} returned by @@ -92,7 +95,7 @@ * PageRequest.ofPage(5) * .size(50) * .afterCursor(Cursor.forKey(emp.lastName, emp.firstName, emp.id)); - * page = employees.findByHoursWorkedGreaterThan(1500, pageRequest); + * page = employees.withOvertime(40, pageRequest); * </pre> * * <p>By making the query for the next page relative to observed values, diff --git a/api/src/main/java/jakarta/data/page/PageRequest.java b/api/src/main/java/jakarta/data/page/PageRequest.java index a73421d1d..f7d90a4b6 100644 --- a/api/src/main/java/jakarta/data/page/PageRequest.java +++ b/api/src/main/java/jakarta/data/page/PageRequest.java @@ -37,20 +37,25 @@ * example:</p> * * <pre> + * @Find * @OrderBy("age") * @OrderBy("ssn") - * Person[] findByAgeBetween(int minAge, int maxAge, PageRequest pageRequest); + * Person[] agedBetween(@By("age") @Is(GreaterThanEqual) int minAge, + * @By("age") @Is(LessThanEqual) int maxAge, + * PageRequest pageRequest); * </pre> * * <p>This method might be called as follows:</p> * * <pre> - * var page = people.findByAgeBetween(35, 59, + * var page = people.agedBetween( + * 35, 59, * PageRequest.ofSize(100)); * var results = page.content(); * ... * while (page.hasNext()) { - * page = people.findByAgeBetween(35, 59, + * page = people.agedBetween( + * 35, 59, * page.nextPageRequest().withoutTotal()); * results = page.content(); * ... diff --git a/api/src/main/java/jakarta/data/repository/By.java b/api/src/main/java/jakarta/data/repository/By.java index 233dec891..cb01804a1 100644 --- a/api/src/main/java/jakarta/data/repository/By.java +++ b/api/src/main/java/jakarta/data/repository/By.java @@ -33,7 +33,10 @@ * to the unique identifier attribute. * </ul> * <p>Arguments to the annotated parameter are compared to values of the - * mapped attribute.</p> + * mapped attribute. The equality comparison is used by default.<p> + * + * <p>For other types of basic comparisons, include the {@link Is} annotation.</p> + * * <p>The attribute name may be a compound name like {@code address.city}.</p> * * <p>For example, for a {@code Person} entity with attributes {@code ssn}, diff --git a/api/src/main/java/jakarta/data/repository/Is.java b/api/src/main/java/jakarta/data/repository/Is.java new file mode 100644 index 000000000..20adb800c --- /dev/null +++ b/api/src/main/java/jakarta/data/repository/Is.java @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2024 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; + +/** + * <p>Annotates a parameter of a repository {@link Find} or {@link Delete} method, + * indicating how a persistent field is compared against the parameter's value. + * The {@link By} annotation is used on the same parameter to identify the + * persistent field.</p> + * + * <p>For example,</p> + * + * <pre> + * @Repository + * public interface Products extends CrudRepository<Product, Long> { + * + * // Find all Product entities where the price field is less than a maximum value. + * @Find + * List<Product> pricedBelow(@By(_Product.PRICE) @Is(LessThan) float max); + * + * // Find a page of Product entities where the name field matches a pattern, ignoring case. + * @Find + * Page<Product> search(@By(_Product.NAME) @Is(LikeIgnoreCase) String pattern, + * PageRequest pagination, + * Order<Product> order); + * + * // Remove Product entities with any of the unique identifiers listed. + * @Delete + * void remove(@By(ID) @Is(In) List<Long> productIds); + * } + * </pre> + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.PARAMETER) +public @interface Is { + /** + * <p>The type of comparison operation to use when comparing a persistent + * field against a value that is supplied to a repository method.</p> + * + * <p>The following example compares the year a person was born against + * a minimum and maximum year that are supplied as parameters to a repository + * method:</p> + * + * <pre> + * @Find + * @OrderBy(_Person.YEAR_BORN) + * List<Person> bornWithin(@By(_Person.YEAR_BORN) @Is(GreaterThanEqual) float minYear, + * @By(_Person.YEAR_BORN) @Is(LessThanEqual) float maxYear); + * </pre> + * + * <p>The default comparison operation is the {@linkplain Op#Equal equality} + * comparison.</p> + * + * @return the type of comparison operation. + */ + Op value() default Op.Equal; + + /** + * <p>Comparison operations for the {@link Is} annotation.</p> + * + * <p>For more concise code, it can be convenient to statically import one + * or more comparison operations. For example:</p> + * + * <pre> + * import static jakarta.data.repository.Is.Op.*; + * </pre> + */ + public static enum Op { + // TODO add JavaDoc with examples to these + Equal, + GreaterThan, + GreaterThanEqual, + IgnoreCase, + In, + LessThan, + LessThanEqual, + Like, + LikeIgnoreCase, + // TODO might want to give more thought to the exact names, + Prefixed, + PrefixedIgnoreCase, + Substringed, + SubstringedIgnoreCase, + Suffixed, + SuffixedIgnoreCase, + Not, + NotIgnoreCase, + NotIn, + NotLike, + NotLikeIgnoreCase, + NotPrefixed, + NotPrefixedIgnoreCase, + NotSubstringed, + NotSubstringedIgnoreCase, + NotSuffixed, + NotSuffixedIgnoreCase; + } +} diff --git a/api/src/main/java/jakarta/data/repository/OrderBy.java b/api/src/main/java/jakarta/data/repository/OrderBy.java index 207afd121..8c1822508 100644 --- a/api/src/main/java/jakarta/data/repository/OrderBy.java +++ b/api/src/main/java/jakarta/data/repository/OrderBy.java @@ -62,8 +62,9 @@ * <p>The default sort order is ascending. The {@code descending} member can be * used to specify the sort direction.</p> * <pre> - * @OrderBy(value = "price", descending = true) - * {@code Stream<Product>} findByPriceLessThanEqual(double maxPrice); + * @Find + * @OrderBy(value = _Product.PRICE, descending = true) + * {@code Stream<Product>} pricedBelow(@By(_Product.PRICE) @Is(LessThan) double maxPrice); * </pre> * * <p>A repository method with an {@code @OrderBy} annotation must not @@ -115,8 +116,9 @@ * <p>For example,</p> * * <pre> + * @Find * @OrderBy("age") - * Stream<Person> findByLastName(String lastName); + * Stream<Person> withLastName(@By("lastName") @Is(IgnoreCase) String surname); * </pre> * * @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 6812adcb2..bfc59513c 100644 --- a/api/src/main/java/jakarta/data/repository/Repository.java +++ b/api/src/main/java/jakarta/data/repository/Repository.java @@ -37,8 +37,9 @@ * @Repository * public interface Products extends DataRepository<Product, Long> { * + * @Find * @OrderBy("price") - * List<Product> findByNameLike(String namePattern); + * List<Product> named(@By("name") @Is(LikeIgnoreCase) String namePattern); * * @Query("UPDATE Product SET price = price - (price * ?1) WHERE price * ?1 <= ?2") * int putOnSale(float rateOfDiscount, float maxDiscount); @@ -52,7 +53,7 @@ * Products products; * * ... - * found = products.findByNameLike("%Printer%"); + * found = products.named("%Printer%"); * numUpdated = products.putOnSale(0.15f, 20.0f); * </pre> * diff --git a/api/src/main/java/module-info.java b/api/src/main/java/module-info.java index cfb4d2f88..1638d5dde 100644 --- a/api/src/main/java/module-info.java +++ b/api/src/main/java/module-info.java @@ -28,6 +28,7 @@ import jakarta.data.repository.Delete; import jakarta.data.repository.Find; import jakarta.data.repository.Insert; +import jakarta.data.repository.Is; import jakarta.data.repository.OrderBy; import jakarta.data.repository.Param; import jakarta.data.repository.Query; @@ -70,8 +71,11 @@ * @Insert * void create(Product prod); * + * @Find * @OrderBy("price") - * List<Product> findByNameIgnoreCaseLikeAndPriceLessThan(String namePattern, float max); + * List<Product> search( + * @By("name") @Is(LikeIgnoreCase) String namePattern, + * @By("price") @Is(LessThanEqual) float max); * * @Query("UPDATE Product SET price = price * (1.0 - ?1) WHERE yearProduced <= ?2") * int discountOldInventory(float rateOfDiscount, int maxYear); @@ -91,7 +95,7 @@ * ... * products.create(newProduct); * - * found = products.findByNameIgnoreCaseLikeAndPriceLessThan("%cell%phone%", 900.0f); + * found = products.search("%cell%phone%", 900.0f); * * numDiscounted = products.discountOldInventory(0.15f, Year.now().getValue() - 1); * </pre> @@ -150,8 +154,10 @@ * * @Repository * public interface Purchases { + * @Find * @OrderBy("address.zipCode") - * List<Purchase> findByAddressZipCodeIn(List<Integer> zipCodes); + * List<Purchase> forZipCodes( + * @By("address.zipCode") @Is(In) List<Integer> zipCodes); * * @Query("WHERE address.zipCode = ?1") * List<Purchase> forZipCode(int zipCode); @@ -711,15 +717,19 @@ * with the {@code -parameters} compiler option so that parameter names are * available at runtime.</p> * - * <p>Each parameter determines a query condition, and each such condition - * is an equality condition. All conditions must match for a record to + * <p>Each parameter determines a query condition. By default, each such condition + * is an equality condition. The {@link Is} annotation can be combined with the + * {@link By} annotation to request other types of basic comparisons for a + * repository method parameter. All conditions must match for a record to * satisfy the query.</p> * * <pre> * @Find * @OrderBy("lastName") * @OrderBy("firstName") - * List<Person> peopleByAgeAndNationality(int age, Country nationality); + * List<Person> ofNationalityAndOlderThan( + * Country nationality, + * @By("age") @Is(GreaterThan) int minAge); * </pre> * * <pre> @@ -727,6 +737,11 @@ * Optional<Person> person(String ssn); * </pre> * + * <pre> + * @Delete + * void remove(@By("status") @Is(In) List<Status> list); + * </pre> + * * <p>The {@code _} character may be used in a method parameter name to * reference an embedded attribute.</p> * @@ -742,11 +757,19 @@ * * <pre> * // Query by Method Name - * Vehicle[] findByMakeAndModelAndYear(String makerName, String model, int year, Sort<?>... sorts); + * Vehicle[] findByMakeAndModelAndYearBetween(String makerName, + * String model, + * int minYear, + * int maxYear, + * Sort<?>... sorts); * * // parameter-based conditions * @Find - * Vehicle[] searchFor(String make, String model, int year, Sort<?>... sorts); + * Vehicle[] search(String make, + * String model, + * @By(_Vehicle.YEAR) @Is(GreaterThanEqual) int minYear, + * @By(_Vehicle.YEAR) @Is(LessThanEqual) int maxYear, + * Sort<?>... sorts); * </pre> * * <p>For further information, refer to the {@linkplain Find API documentation} @@ -784,11 +807,13 @@ * allows its results to be split and retrieved in pages. For example,</p> * * <pre> - * Product[] findByNameLikeOrderByAmountSoldDescIdAsc( - * String pattern, PageRequest pageRequest); + * @Find + * @OrderBy(value = _Product.AMOUNT_SOLD, descending = true) + * @OrderBy(ID) + * Product[] named(@By(_Product.NAME) @Is(LikeIgnoreCase) String pattern, + * PageRequest pageRequest); * ... - * page1 = products.findByNameLikeOrderByAmountSoldDescIdAsc( - * "%phone%", PageRequest.ofSize(20)); + * page1 = products.named("%phone%", PageRequest.ofSize(20)); * </pre> * * <p>When using pagination, always ensure that the ordering is consistent @@ -802,16 +827,17 @@ * For example,</p> * * <pre> - * Product[] findByNameLikeAndPriceBetween(String pattern, - * float minPrice, - * float maxPrice, - * PageRequest pageRequest, - * Order<Product> order); + * @Find + * Product[] search(@By("name") @Is(LikeIgnoreCase) String pattern, + * @By("price") @Is(GreaterThanEqual) float minPrice, + * @By("price") @Is(LessThanEqual) float maxPrice, + * PageRequest pageRequest, + * Order<Product> order); * * ... * PageRequest page1Request = PageRequest.ofSize(25); * - * page1 = products.findByNameLikeAndPriceBetween( + * page1 = products.search( * namePattern, minPrice, maxPrice, page1Request, * Order.by(Sort.desc("price"), Sort.asc("id")); * </pre> @@ -821,13 +847,17 @@ * of {@link Sort} and passed to the repository find method. For example,</p> * * <pre> - * Product[] findByNameLike(String pattern, Limit max, Order<Product> sortBy); + * @Find + * Product[] named(@By("name") @Is(LikeIgnoreCase) String pattern, + * Limit max, + * Order<Product> sortBy); * * ... - * found = products.findByNameLike(namePattern, Limit.of(25), - * Order.by(Sort.desc("price"), - * Sort.desc("amountSold"), - * Sort.asc("id"))); + * found = products.named(namePattern, + * Limit.of(25), + * Order.by(Sort.desc("price"), + * Sort.desc("amountSold"), + * Sort.asc("id"))); * </pre> * * <p>Generic, untyped {@link Sort} criteria can be supplied directly to a @@ -835,13 +865,17 @@ * For example,</p> * * <pre> - * Product[] findByNameLike(String pattern, Limit max, {@code Sort<?>...} sortBy); + * @Find + * Product[] named(@By("name") @Is(LikeIgnoreCase) String pattern, + * Limit max, + * {@code Sort<?>...} sortBy); * * ... - * found = products.findByNameLike(namePattern, Limit.of(25), - * Sort.desc("price"), - * Sort.desc("amountSold"), - * Sort.asc("name")); + * found = products.named(namePattern, + * Limit.of(25), + * Sort.desc("price"), + * Sort.desc("amountSold"), + * Sort.asc("name")); * </pre> * * <h2>Returning subsets of entity attributes</h2> From e9e7e1c4be395f3910bf02d8b4dcc7c027b7a608 Mon Sep 17 00:00:00 2001 From: Nathan Rauh <nathan.rauh@us.ibm.com> Date: Thu, 7 Nov 2024 09:56:19 -0600 Subject: [PATCH 2/4] Switch to String constants with capitalized names --- api/src/main/java/jakarta/data/Limit.java | 2 +- .../java/jakarta/data/page/CursoredPage.java | 2 +- .../java/jakarta/data/page/PageRequest.java | 4 +- .../main/java/jakarta/data/repository/Is.java | 81 ++++++++----------- .../java/jakarta/data/repository/OrderBy.java | 4 +- .../jakarta/data/repository/Repository.java | 2 +- api/src/main/java/module-info.java | 26 +++--- 7 files changed, 53 insertions(+), 68 deletions(-) diff --git a/api/src/main/java/jakarta/data/Limit.java b/api/src/main/java/jakarta/data/Limit.java index d06981a2e..33270b342 100644 --- a/api/src/main/java/jakarta/data/Limit.java +++ b/api/src/main/java/jakarta/data/Limit.java @@ -34,7 +34,7 @@ * * <pre> * @Find - * Product[] named(@By(_Product.NAME) @Is(LikeIgnoreCase) String namePattern, + * Product[] named(@By(_Product.NAME) @Is(LIKE_IGNORE_CASE) String namePattern, * Limit limit, * Sort<Product>... sorts); * diff --git a/api/src/main/java/jakarta/data/page/CursoredPage.java b/api/src/main/java/jakarta/data/page/CursoredPage.java index 71770f8ce..4716e0c3b 100644 --- a/api/src/main/java/jakarta/data/page/CursoredPage.java +++ b/api/src/main/java/jakarta/data/page/CursoredPage.java @@ -60,7 +60,7 @@ * @OrderBy(_Employee.FIRSTNAME) * @OrderBy(_Employee.ID) * CursoredPage<Employee> withOvertime( - * @By(_Employee.HOURSWORKED) @Is(GreaterThan) int fullTimeHours, + * @By(_Employee.HOURSWORKED) @Is(GREATER_THAN) int fullTimeHours, * PageRequest pageRequest); * </pre> * diff --git a/api/src/main/java/jakarta/data/page/PageRequest.java b/api/src/main/java/jakarta/data/page/PageRequest.java index f7d90a4b6..b779a9369 100644 --- a/api/src/main/java/jakarta/data/page/PageRequest.java +++ b/api/src/main/java/jakarta/data/page/PageRequest.java @@ -40,8 +40,8 @@ * @Find * @OrderBy("age") * @OrderBy("ssn") - * Person[] agedBetween(@By("age") @Is(GreaterThanEqual) int minAge, - * @By("age") @Is(LessThanEqual) int maxAge, + * Person[] agedBetween(@By("age") @Is(GREATER_THAN_EQ) int minAge, + * @By("age") @Is(LESS_THAN_EQ) int maxAge, * PageRequest pageRequest); * </pre> * diff --git a/api/src/main/java/jakarta/data/repository/Is.java b/api/src/main/java/jakarta/data/repository/Is.java index 20adb800c..ac1cfea1b 100644 --- a/api/src/main/java/jakarta/data/repository/Is.java +++ b/api/src/main/java/jakarta/data/repository/Is.java @@ -36,26 +36,45 @@ * * // Find all Product entities where the price field is less than a maximum value. * @Find - * List<Product> pricedBelow(@By(_Product.PRICE) @Is(LessThan) float max); + * List<Product> pricedBelow(@By(_Product.PRICE) @Is(LESS_THAN) float max); * * // Find a page of Product entities where the name field matches a pattern, ignoring case. * @Find - * Page<Product> search(@By(_Product.NAME) @Is(LikeIgnoreCase) String pattern, + * Page<Product> search(@By(_Product.NAME) @Is(LIKE_IGNORE_CASE) String pattern, * PageRequest pagination, * Order<Product> order); * * // Remove Product entities with any of the unique identifiers listed. * @Delete - * void remove(@By(ID) @Is(In) List<Long> productIds); + * void remove(@By(ID) @Is(IN) List<Long> productIds); * } * </pre> */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.PARAMETER) public @interface Is { + // TODO add JavaDoc with examples to these + String EQUAL = "EQUAL"; + String GREATER_THAN = "GREATER_THAN"; + String GREATER_THAN_EQ = "GREATER_THAN_EQ"; + String IGNORE_CASE = "IGNORE_CASE"; + String IN = "IN"; + String LESS_THAN = "LESS_THAN"; + String LESS_THAN_EQ = "LESS_THAN_EQ"; + String LIKE = "LIKE"; + String LIKE_IGNORE_CASE = "LIKE_IGNORE_CASE"; + String NOT = "NOT"; + String NOT_IGNORE_CASE = "NOT_IGNORE_CASE"; + String NOT_IN = "NOT_IN"; + String NOT_LIKE = "NOT_LIKE"; + String NOT_LIKE_IGNORE_CASE = "NOT_LIKE_IGNORE_CASE"; + /** * <p>The type of comparison operation to use when comparing a persistent - * field against a value that is supplied to a repository method.</p> + * field against a value that is supplied to a repository method. + * For portable applications, the comparison operation must be one of the + * constants defined within this class. Jakarta Data providers might choose + * to provide their own constants as non-portable extensions.</p> * * <p>The following example compares the year a person was born against * a minimum and maximum year that are supplied as parameters to a repository @@ -64,55 +83,21 @@ * <pre> * @Find * @OrderBy(_Person.YEAR_BORN) - * List<Person> bornWithin(@By(_Person.YEAR_BORN) @Is(GreaterThanEqual) float minYear, - * @By(_Person.YEAR_BORN) @Is(LessThanEqual) float maxYear); + * List<Person> bornWithin(@By(_Person.YEAR_BORN) @Is(TREATER_THAN_EQ) float minYear, + * @By(_Person.YEAR_BORN) @Is(LESS_THAN_EQ) float maxYear); * </pre> * - * <p>The default comparison operation is the {@linkplain Op#Equal equality} - * comparison.</p> - * - * @return the type of comparison operation. - */ - Op value() default Op.Equal; - - /** - * <p>Comparison operations for the {@link Is} annotation.</p> + * <p>The default comparison operation is the {@linkplain #EQUAL equality} + * comparison.</p> * - * <p>For more concise code, it can be convenient to statically import one - * or more comparison operations. For example:</p> + * <p>For concise code, it can be convenient for a repository interface to + * statically import one or more constants from this class. For example:</p> * * <pre> - * import static jakarta.data.repository.Is.Op.*; + * import static jakarta.data.repository.Is.*; * </pre> + * + * @return the type of comparison operation. */ - public static enum Op { - // TODO add JavaDoc with examples to these - Equal, - GreaterThan, - GreaterThanEqual, - IgnoreCase, - In, - LessThan, - LessThanEqual, - Like, - LikeIgnoreCase, - // TODO might want to give more thought to the exact names, - Prefixed, - PrefixedIgnoreCase, - Substringed, - SubstringedIgnoreCase, - Suffixed, - SuffixedIgnoreCase, - Not, - NotIgnoreCase, - NotIn, - NotLike, - NotLikeIgnoreCase, - NotPrefixed, - NotPrefixedIgnoreCase, - NotSubstringed, - NotSubstringedIgnoreCase, - NotSuffixed, - NotSuffixedIgnoreCase; - } + String value() default EQUAL; } diff --git a/api/src/main/java/jakarta/data/repository/OrderBy.java b/api/src/main/java/jakarta/data/repository/OrderBy.java index 8c1822508..c6ce93653 100644 --- a/api/src/main/java/jakarta/data/repository/OrderBy.java +++ b/api/src/main/java/jakarta/data/repository/OrderBy.java @@ -64,7 +64,7 @@ * <pre> * @Find * @OrderBy(value = _Product.PRICE, descending = true) - * {@code Stream<Product>} pricedBelow(@By(_Product.PRICE) @Is(LessThan) double maxPrice); + * {@code Stream<Product>} pricedBelow(@By(_Product.PRICE) @Is(LESS_THAN) double maxPrice); * </pre> * * <p>A repository method with an {@code @OrderBy} annotation must not @@ -118,7 +118,7 @@ * <pre> * @Find * @OrderBy("age") - * Stream<Person> withLastName(@By("lastName") @Is(IgnoreCase) String surname); + * Stream<Person> withLastName(@By("lastName") @Is(IGNORE_CASE) String surname); * </pre> * * @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 bfc59513c..93d7bb82d 100644 --- a/api/src/main/java/jakarta/data/repository/Repository.java +++ b/api/src/main/java/jakarta/data/repository/Repository.java @@ -39,7 +39,7 @@ * * @Find * @OrderBy("price") - * List<Product> named(@By("name") @Is(LikeIgnoreCase) String namePattern); + * List<Product> named(@By("name") @Is(LIKE_IGNORE_CASE) 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/module-info.java b/api/src/main/java/module-info.java index 1638d5dde..789c762ef 100644 --- a/api/src/main/java/module-info.java +++ b/api/src/main/java/module-info.java @@ -74,8 +74,8 @@ * @Find * @OrderBy("price") * List<Product> search( - * @By("name") @Is(LikeIgnoreCase) String namePattern, - * @By("price") @Is(LessThanEqual) float max); + * @By("name") @Is(LIKE_IGNORE_CASE) String namePattern, + * @By("price") @Is(lESS_THAN_EQ) float max); * * @Query("UPDATE Product SET price = price * (1.0 - ?1) WHERE yearProduced <= ?2") * int discountOldInventory(float rateOfDiscount, int maxYear); @@ -157,7 +157,7 @@ * @Find * @OrderBy("address.zipCode") * List<Purchase> forZipCodes( - * @By("address.zipCode") @Is(In) List<Integer> zipCodes); + * @By("address.zipCode") @Is(IN) List<Integer> zipCodes); * * @Query("WHERE address.zipCode = ?1") * List<Purchase> forZipCode(int zipCode); @@ -729,7 +729,7 @@ * @OrderBy("firstName") * List<Person> ofNationalityAndOlderThan( * Country nationality, - * @By("age") @Is(GreaterThan) int minAge); + * @By("age") @Is(GREATER_THAN) int minAge); * </pre> * * <pre> @@ -739,7 +739,7 @@ * * <pre> * @Delete - * void remove(@By("status") @Is(In) List<Status> list); + * void remove(@By("status") @Is(IN) List<Status> list); * </pre> * * <p>The {@code _} character may be used in a method parameter name to @@ -767,8 +767,8 @@ * @Find * Vehicle[] search(String make, * String model, - * @By(_Vehicle.YEAR) @Is(GreaterThanEqual) int minYear, - * @By(_Vehicle.YEAR) @Is(LessThanEqual) int maxYear, + * @By(_Vehicle.YEAR) @Is(GREATER_THAN_EQ) int minYear, + * @By(_Vehicle.YEAR) @Is(LESS_THAN_EQ) int maxYear, * Sort<?>... sorts); * </pre> * @@ -810,7 +810,7 @@ * @Find * @OrderBy(value = _Product.AMOUNT_SOLD, descending = true) * @OrderBy(ID) - * Product[] named(@By(_Product.NAME) @Is(LikeIgnoreCase) String pattern, + * Product[] named(@By(_Product.NAME) @Is(LIKE_IGNORE_CASE) String pattern, * PageRequest pageRequest); * ... * page1 = products.named("%phone%", PageRequest.ofSize(20)); @@ -828,9 +828,9 @@ * * <pre> * @Find - * Product[] search(@By("name") @Is(LikeIgnoreCase) String pattern, - * @By("price") @Is(GreaterThanEqual) float minPrice, - * @By("price") @Is(LessThanEqual) float maxPrice, + * Product[] search(@By("name") @Is(LIKE_IGNORE_CASE) String pattern, + * @By("price") @Is(GREATER_THAN_EQ) float minPrice, + * @By("price") @Is(LESS_THAN_EQ) float maxPrice, * PageRequest pageRequest, * Order<Product> order); * @@ -848,7 +848,7 @@ * * <pre> * @Find - * Product[] named(@By("name") @Is(LikeIgnoreCase) String pattern, + * Product[] named(@By("name") @Is(LIKE_IGNORE_CASE) String pattern, * Limit max, * Order<Product> sortBy); * @@ -866,7 +866,7 @@ * * <pre> * @Find - * Product[] named(@By("name") @Is(LikeIgnoreCase) String pattern, + * Product[] named(@By("name") @Is(LIKE_IGNORE_CASE) String pattern, * Limit max, * {@code Sort<?>...} sortBy); * From cd19343c5a3d25be57f40e2ca186e5abb21ff88f Mon Sep 17 00:00:00 2001 From: Nathan Rauh <nathan.rauh@us.ibm.com> Date: Fri, 8 Nov 2024 13:44:03 -0600 Subject: [PATCH 3/4] Put the enumeration back, but keep the upper case, and shorten IGNORE_CASE to ANY_CASE --- api/src/main/java/jakarta/data/Limit.java | 2 +- .../main/java/jakarta/data/repository/Is.java | 78 +++++++++++++------ .../java/jakarta/data/repository/OrderBy.java | 2 +- .../jakarta/data/repository/Repository.java | 2 +- api/src/main/java/module-info.java | 10 +-- 5 files changed, 61 insertions(+), 33 deletions(-) diff --git a/api/src/main/java/jakarta/data/Limit.java b/api/src/main/java/jakarta/data/Limit.java index 33270b342..ddd1a150d 100644 --- a/api/src/main/java/jakarta/data/Limit.java +++ b/api/src/main/java/jakarta/data/Limit.java @@ -34,7 +34,7 @@ * * <pre> * @Find - * Product[] named(@By(_Product.NAME) @Is(LIKE_IGNORE_CASE) String namePattern, + * Product[] named(@By(_Product.NAME) @Is(LIKE_ANY_CASE) String namePattern, * Limit limit, * Sort<Product>... sorts); * diff --git a/api/src/main/java/jakarta/data/repository/Is.java b/api/src/main/java/jakarta/data/repository/Is.java index ac1cfea1b..c12eade51 100644 --- a/api/src/main/java/jakarta/data/repository/Is.java +++ b/api/src/main/java/jakarta/data/repository/Is.java @@ -25,8 +25,10 @@ /** * <p>Annotates a parameter of a repository {@link Find} or {@link Delete} method, * indicating how a persistent field is compared against the parameter's value. - * The {@link By} annotation is used on the same parameter to identify the - * persistent field.</p> + * The {@link By} annotation can be used on the same parameter to identify the + * persistent field. Otherwise, if the {@code -parameters} compile option is + * enabled, the the persistent field is inferred by matching the name of the + * method parameter.</p> * * <p>For example,</p> * @@ -40,7 +42,7 @@ * * // Find a page of Product entities where the name field matches a pattern, ignoring case. * @Find - * Page<Product> search(@By(_Product.NAME) @Is(LIKE_IGNORE_CASE) String pattern, + * Page<Product> search(@By(_Product.NAME) @Is(LIKE_ANY_CASE) String pattern, * PageRequest pagination, * Order<Product> order); * @@ -53,28 +55,10 @@ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.PARAMETER) public @interface Is { - // TODO add JavaDoc with examples to these - String EQUAL = "EQUAL"; - String GREATER_THAN = "GREATER_THAN"; - String GREATER_THAN_EQ = "GREATER_THAN_EQ"; - String IGNORE_CASE = "IGNORE_CASE"; - String IN = "IN"; - String LESS_THAN = "LESS_THAN"; - String LESS_THAN_EQ = "LESS_THAN_EQ"; - String LIKE = "LIKE"; - String LIKE_IGNORE_CASE = "LIKE_IGNORE_CASE"; - String NOT = "NOT"; - String NOT_IGNORE_CASE = "NOT_IGNORE_CASE"; - String NOT_IN = "NOT_IN"; - String NOT_LIKE = "NOT_LIKE"; - String NOT_LIKE_IGNORE_CASE = "NOT_LIKE_IGNORE_CASE"; /** * <p>The type of comparison operation to use when comparing a persistent - * field against a value that is supplied to a repository method. - * For portable applications, the comparison operation must be one of the - * constants defined within this class. Jakarta Data providers might choose - * to provide their own constants as non-portable extensions.</p> + * field against a value that is supplied to a repository method..</p> * * <p>The following example compares the year a person was born against * a minimum and maximum year that are supplied as parameters to a repository @@ -83,7 +67,7 @@ * <pre> * @Find * @OrderBy(_Person.YEAR_BORN) - * List<Person> bornWithin(@By(_Person.YEAR_BORN) @Is(TREATER_THAN_EQ) float minYear, + * List<Person> bornWithin(@By(_Person.YEAR_BORN) @Is(GREATER_THAN_EQ) float minYear, * @By(_Person.YEAR_BORN) @Is(LESS_THAN_EQ) float maxYear); * </pre> * @@ -94,10 +78,54 @@ * statically import one or more constants from this class. For example:</p> * * <pre> - * import static jakarta.data.repository.Is.*; + * import static jakarta.data.repository.Is.Op.*; * </pre> * * @return the type of comparison operation. */ - String value() default EQUAL; + Op value() default Op.EQUAL; + + /** + * <p>Comparison operations for the {@link Is} annotation.</p> + * + * <p>For more concise code, it can be convenient to statically import one + * or more comparison operations. For example:</p> + * + * <pre> + * import static jakarta.data.repository.Is.Op.*; + * </pre> + */ + public static enum Op { + // TODO add JavaDoc with examples to these + ANY_CASE, + EQUAL, + GREATER_THAN, + GREATER_THAN_ANY_CASE, + GREATER_THAN_EQ, + GREATER_THAN_EQ_ANY_CASE, + IN, + LESS_THAN, + LESS_THAN_ANY_CASE, + LESS_THAN_EQ, + LESS_THAN_EQ_ANY_CASE, + LIKE, + LIKE_ANY_CASE, + PREFIXED, + PREFIXED_ANY_CASE, + SUBSTRINGED, + SUBSTRINGED_ANY_CASE, + SUFFIXED, + SUFFIXED_ANY_CASE, + NOT, + NOT_ANY_CASE, + NOT_IN, + NOT_LIKE, + NOT_LIKE_ANY_CASE, + NOT_PREFIXED, + NOT_PREFIXED_ANY_CASE, + NOT_SUBSTRINGED, + NOT_SUBSTRINGED_ANY_CASE, + NOT_SUFFIXED, + NOT_SUFFIXED_ANY_CASE; + } } diff --git a/api/src/main/java/jakarta/data/repository/OrderBy.java b/api/src/main/java/jakarta/data/repository/OrderBy.java index c6ce93653..8d84373ec 100644 --- a/api/src/main/java/jakarta/data/repository/OrderBy.java +++ b/api/src/main/java/jakarta/data/repository/OrderBy.java @@ -118,7 +118,7 @@ * <pre> * @Find * @OrderBy("age") - * Stream<Person> withLastName(@By("lastName") @Is(IGNORE_CASE) String surname); + * Stream<Person> withLastName(@By("lastName") @Is(ANY_CASE) String surname); * </pre> * * @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 93d7bb82d..d020f4d4e 100644 --- a/api/src/main/java/jakarta/data/repository/Repository.java +++ b/api/src/main/java/jakarta/data/repository/Repository.java @@ -39,7 +39,7 @@ * * @Find * @OrderBy("price") - * List<Product> named(@By("name") @Is(LIKE_IGNORE_CASE) String namePattern); + * List<Product> named(@By("name") @Is(LIKE_ANY_CASE) 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/module-info.java b/api/src/main/java/module-info.java index 789c762ef..87281f858 100644 --- a/api/src/main/java/module-info.java +++ b/api/src/main/java/module-info.java @@ -74,7 +74,7 @@ * @Find * @OrderBy("price") * List<Product> search( - * @By("name") @Is(LIKE_IGNORE_CASE) String namePattern, + * @By("name") @Is(LIKE_ANY_CASE) String namePattern, * @By("price") @Is(lESS_THAN_EQ) float max); * * @Query("UPDATE Product SET price = price * (1.0 - ?1) WHERE yearProduced <= ?2") @@ -810,7 +810,7 @@ * @Find * @OrderBy(value = _Product.AMOUNT_SOLD, descending = true) * @OrderBy(ID) - * Product[] named(@By(_Product.NAME) @Is(LIKE_IGNORE_CASE) String pattern, + * Product[] named(@By(_Product.NAME) @Is(LIKE_ANY_CASE) String pattern, * PageRequest pageRequest); * ... * page1 = products.named("%phone%", PageRequest.ofSize(20)); @@ -828,7 +828,7 @@ * * <pre> * @Find - * Product[] search(@By("name") @Is(LIKE_IGNORE_CASE) String pattern, + * Product[] search(@By("name") @Is(LIKE_ANY_CASE) String pattern, * @By("price") @Is(GREATER_THAN_EQ) float minPrice, * @By("price") @Is(LESS_THAN_EQ) float maxPrice, * PageRequest pageRequest, @@ -848,7 +848,7 @@ * * <pre> * @Find - * Product[] named(@By("name") @Is(LIKE_IGNORE_CASE) String pattern, + * Product[] named(@By("name") @Is(LIKE_ANY_CASE) String pattern, * Limit max, * Order<Product> sortBy); * @@ -866,7 +866,7 @@ * * <pre> * @Find - * Product[] named(@By("name") @Is(LIKE_IGNORE_CASE) String pattern, + * Product[] named(@By("name") @Is(LIKE_ANY_CASE) String pattern, * Limit max, * {@code Sort<?>...} sortBy); * From a55eebad5c5bc65cec7e25bc6c68442a29d98e68 Mon Sep 17 00:00:00 2001 From: Nathan Rauh <nathan.rauh@us.ibm.com> Date: Mon, 17 Feb 2025 11:33:02 -0600 Subject: [PATCH 4/4] Reuse existing Operator enum --- api/src/main/java/jakarta/data/Limit.java | 2 +- .../data/metamodel/restrict/Operator.java | 23 ++++++- .../java/jakarta/data/page/PageRequest.java | 4 +- .../jakarta/data/repository/IgnoreCase.java | 60 +++++++++++++++++++ .../main/java/jakarta/data/repository/Is.java | 60 +++---------------- .../java/jakarta/data/repository/OrderBy.java | 2 +- .../jakarta/data/repository/Repository.java | 2 +- api/src/main/java/module-info.java | 20 +++---- 8 files changed, 106 insertions(+), 67 deletions(-) create mode 100644 api/src/main/java/jakarta/data/repository/IgnoreCase.java diff --git a/api/src/main/java/jakarta/data/Limit.java b/api/src/main/java/jakarta/data/Limit.java index ddd1a150d..7f144f252 100644 --- a/api/src/main/java/jakarta/data/Limit.java +++ b/api/src/main/java/jakarta/data/Limit.java @@ -34,7 +34,7 @@ * * <pre> * @Find - * Product[] named(@By(_Product.NAME) @Is(LIKE_ANY_CASE) String namePattern, + * Product[] named(@By(_Product.NAME) @Is(LIKE) @IgnoreCase String namePattern, * Limit limit, * Sort<Product>... sorts); * diff --git a/api/src/main/java/jakarta/data/metamodel/restrict/Operator.java b/api/src/main/java/jakarta/data/metamodel/restrict/Operator.java index 8cb178c10..84015544d 100644 --- a/api/src/main/java/jakarta/data/metamodel/restrict/Operator.java +++ b/api/src/main/java/jakarta/data/metamodel/restrict/Operator.java @@ -27,7 +27,13 @@ public enum Operator { LIKE, NOT_EQUAL, NOT_IN, - NOT_LIKE; + NOT_LIKE, + NOT_PREFIXED, + NOT_SUBSTRINGED, + NOT_SUFFIXED, + PREFIXED, + SUBSTRINGED, + SUFFIXED; /** * Representation of the operator as it appears in query language. @@ -35,6 +41,9 @@ public enum Operator { * in query langugae. * * @return the representation of the operator in query language. + * For operators that have a more complex representation in + * query language, this method returns the {@link #name()} + * of the operator. */ String asQueryLanguage() { return switch (this) { @@ -48,6 +57,12 @@ String asQueryLanguage() { case NOT_EQUAL -> "<>"; case NOT_IN -> "NOT IN"; case NOT_LIKE -> "NOT LIKE"; + case NOT_PREFIXED -> NOT_PREFIXED.name(); + case NOT_SUBSTRINGED -> NOT_SUBSTRINGED.name(); + case NOT_SUFFIXED -> NOT_SUFFIXED.name(); + case PREFIXED -> PREFIXED.name(); + case SUBSTRINGED -> SUBSTRINGED.name(); + case SUFFIXED -> SUFFIXED.name(); }; } @@ -68,6 +83,12 @@ Operator negate() { case NOT_EQUAL -> EQUAL; case NOT_IN -> IN; case NOT_LIKE -> LIKE; + case NOT_PREFIXED -> PREFIXED; + case NOT_SUBSTRINGED -> SUBSTRINGED; + case NOT_SUFFIXED -> SUFFIXED; + case PREFIXED -> NOT_PREFIXED; + case SUBSTRINGED -> NOT_SUBSTRINGED; + case SUFFIXED -> NOT_SUFFIXED; }; } } diff --git a/api/src/main/java/jakarta/data/page/PageRequest.java b/api/src/main/java/jakarta/data/page/PageRequest.java index b779a9369..d88dc6a9c 100644 --- a/api/src/main/java/jakarta/data/page/PageRequest.java +++ b/api/src/main/java/jakarta/data/page/PageRequest.java @@ -40,8 +40,8 @@ * @Find * @OrderBy("age") * @OrderBy("ssn") - * Person[] agedBetween(@By("age") @Is(GREATER_THAN_EQ) int minAge, - * @By("age") @Is(LESS_THAN_EQ) int maxAge, + * Person[] agedBetween(@By("age") @Is(GREATER_THAN_EQUAL) int minAge, + * @By("age") @Is(LESS_THAN_EQUAL) int maxAge, * PageRequest pageRequest); * </pre> * 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..da69c020c --- /dev/null +++ b/api/src/main/java/jakarta/data/repository/IgnoreCase.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2024,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.metamodel.restrict.Operator; + +/** + * <p>Annotates a parameter of a repository {@link Find} or {@link Delete} method, + * indicating that a persistent field should be compared ignoring case. + * The {@link By} annotation can be used on the same parameter to identify the + * persistent field. Otherwise, if the {@code -parameters} compile option is + * enabled, the the persistent field is inferred by matching the name of the + * method parameter. The {@link Operator#EQUAL EQUAL} comparison is assumed + * unless the {@link Is} annotation is used on the same parameter to choose a + * different type of comparison.</p> + * + * <p>For example,</p> + * + * <pre> + * @Repository + * public interface People extends BasicRepository<Person, Long> { + * + * // Find all Person entities where the lastName matches the respective value + * // ignoring case. + * @Find + * List<Person> withSurname(@By(_Person.LASTNAME) @IgnoreCase String surname); + * + * // Find a page of Person entities where the lastName field begins with the + * // supplied prefix, ignoring case. + * @Find + * Page<Person> withSurnamePrefix(@By(_Product.LASTNAME) @Is(PREFIXED) @IgnoreCase String prefix, + * PageRequest pagination, + * Order<Person> order); + * } + * </pre> + */ +@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 c12eade51..3d3d6ba43 100644 --- a/api/src/main/java/jakarta/data/repository/Is.java +++ b/api/src/main/java/jakarta/data/repository/Is.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Contributors to the Eclipse Foundation + * Copyright (c) 2024,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. @@ -22,6 +22,8 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import jakarta.data.metamodel.restrict.Operator; + /** * <p>Annotates a parameter of a repository {@link Find} or {@link Delete} method, * indicating how a persistent field is compared against the parameter's value. @@ -42,7 +44,7 @@ * * // Find a page of Product entities where the name field matches a pattern, ignoring case. * @Find - * Page<Product> search(@By(_Product.NAME) @Is(LIKE_ANY_CASE) String pattern, + * Page<Product> search(@By(_Product.NAME) @Is(LIKE) @IgnoreCase String pattern, * PageRequest pagination, * Order<Product> order); * @@ -67,65 +69,21 @@ * <pre> * @Find * @OrderBy(_Person.YEAR_BORN) - * List<Person> bornWithin(@By(_Person.YEAR_BORN) @Is(GREATER_THAN_EQ) float minYear, - * @By(_Person.YEAR_BORN) @Is(LESS_THAN_EQ) float maxYear); + * List<Person> bornWithin(@By(_Person.YEAR_BORN) @Is(GREATER_THAN_EQUAL) float minYear, + * @By(_Person.YEAR_BORN) @Is(LESS_THAN_EQUAL) float maxYear); * </pre> * - * <p>The default comparison operation is the {@linkplain #EQUAL equality} + * <p>The default comparison operation is the {@linkplain Operator#EQUAL equality} * comparison.</p> * * <p>For concise code, it can be convenient for a repository interface to * statically import one or more constants from this class. For example:</p> * * <pre> - * import static jakarta.data.repository.Is.Op.*; + * import static jakarta.data.metamodel.restrict.Operator.*; * </pre> * * @return the type of comparison operation. */ - Op value() default Op.EQUAL; - - /** - * <p>Comparison operations for the {@link Is} annotation.</p> - * - * <p>For more concise code, it can be convenient to statically import one - * or more comparison operations. For example:</p> - * - * <pre> - * import static jakarta.data.repository.Is.Op.*; - * </pre> - */ - public static enum Op { - // TODO add JavaDoc with examples to these - ANY_CASE, - EQUAL, - GREATER_THAN, - GREATER_THAN_ANY_CASE, - GREATER_THAN_EQ, - GREATER_THAN_EQ_ANY_CASE, - IN, - LESS_THAN, - LESS_THAN_ANY_CASE, - LESS_THAN_EQ, - LESS_THAN_EQ_ANY_CASE, - LIKE, - LIKE_ANY_CASE, - PREFIXED, - PREFIXED_ANY_CASE, - SUBSTRINGED, - SUBSTRINGED_ANY_CASE, - SUFFIXED, - SUFFIXED_ANY_CASE, - NOT, - NOT_ANY_CASE, - NOT_IN, - NOT_LIKE, - NOT_LIKE_ANY_CASE, - NOT_PREFIXED, - NOT_PREFIXED_ANY_CASE, - NOT_SUBSTRINGED, - NOT_SUBSTRINGED_ANY_CASE, - NOT_SUFFIXED, - NOT_SUFFIXED_ANY_CASE; - } + Operator value() default Operator.EQUAL; } diff --git a/api/src/main/java/jakarta/data/repository/OrderBy.java b/api/src/main/java/jakarta/data/repository/OrderBy.java index 8d84373ec..ff1e1c6cc 100644 --- a/api/src/main/java/jakarta/data/repository/OrderBy.java +++ b/api/src/main/java/jakarta/data/repository/OrderBy.java @@ -118,7 +118,7 @@ * <pre> * @Find * @OrderBy("age") - * Stream<Person> withLastName(@By("lastName") @Is(ANY_CASE) String surname); + * Stream<Person> withLastName(@By("lastName") @IgnoreCase String surname); * </pre> * * @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 d020f4d4e..f3e820680 100644 --- a/api/src/main/java/jakarta/data/repository/Repository.java +++ b/api/src/main/java/jakarta/data/repository/Repository.java @@ -39,7 +39,7 @@ * * @Find * @OrderBy("price") - * List<Product> named(@By("name") @Is(LIKE_ANY_CASE) String namePattern); + * List<Product> named(@By("name") @Is(LIKE) @IgnoreCase 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/module-info.java b/api/src/main/java/module-info.java index 87281f858..4be5dae70 100644 --- a/api/src/main/java/module-info.java +++ b/api/src/main/java/module-info.java @@ -74,8 +74,8 @@ * @Find * @OrderBy("price") * List<Product> search( - * @By("name") @Is(LIKE_ANY_CASE) String namePattern, - * @By("price") @Is(lESS_THAN_EQ) float max); + * @By("name") @Is(LIKE) @IgnoreCase String namePattern, + * @By("price") @Is(lESS_THAN_EQUAL) float max); * * @Query("UPDATE Product SET price = price * (1.0 - ?1) WHERE yearProduced <= ?2") * int discountOldInventory(float rateOfDiscount, int maxYear); @@ -767,8 +767,8 @@ * @Find * Vehicle[] search(String make, * String model, - * @By(_Vehicle.YEAR) @Is(GREATER_THAN_EQ) int minYear, - * @By(_Vehicle.YEAR) @Is(LESS_THAN_EQ) int maxYear, + * @By(_Vehicle.YEAR) @Is(GREATER_THAN_EQUAL) int minYear, + * @By(_Vehicle.YEAR) @Is(LESS_THAN_EQUAL) int maxYear, * Sort<?>... sorts); * </pre> * @@ -810,7 +810,7 @@ * @Find * @OrderBy(value = _Product.AMOUNT_SOLD, descending = true) * @OrderBy(ID) - * Product[] named(@By(_Product.NAME) @Is(LIKE_ANY_CASE) String pattern, + * Product[] named(@By(_Product.NAME) @Is(LIKE) @IgnoreCase String pattern, * PageRequest pageRequest); * ... * page1 = products.named("%phone%", PageRequest.ofSize(20)); @@ -828,9 +828,9 @@ * * <pre> * @Find - * Product[] search(@By("name") @Is(LIKE_ANY_CASE) String pattern, - * @By("price") @Is(GREATER_THAN_EQ) float minPrice, - * @By("price") @Is(LESS_THAN_EQ) float maxPrice, + * Product[] search(@By("name") @Is(LIKE) @IgnoreCase String pattern, + * @By("price") @Is(GREATER_THAN_EQUAL) float minPrice, + * @By("price") @Is(LESS_THAN_EQUAL) float maxPrice, * PageRequest pageRequest, * Order<Product> order); * @@ -848,7 +848,7 @@ * * <pre> * @Find - * Product[] named(@By("name") @Is(LIKE_ANY_CASE) String pattern, + * Product[] named(@By("name") @Is(LIKE) @IgnoreCase String pattern, * Limit max, * Order<Product> sortBy); * @@ -866,7 +866,7 @@ * * <pre> * @Find - * Product[] named(@By("name") @Is(LIKE_ANY_CASE) String pattern, + * Product[] named(@By("name") @Is(LIKE) @IgnoreCase String pattern, * Limit max, * {@code Sort<?>...} sortBy); *