Skip to content

other types of comparisons for parameter based automatic query #892

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

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions api/src/main/java/jakarta/data/Limit.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,15 @@
* example,</p>
*
* <pre>
* Product[] findByNameLike(String namePattern, Limit limit, Sort&lt;?&gt;... sorts);
* &#64;Find
* Product[] namedLike(&#64;By(_Product.NAME) &#64;Is(Like.class) String namePattern,
* Limit limit,
* Sort&lt;?&gt;... sorts);
*
* ...
* mostExpensive50 = products.findByNameLike(pattern, Limit.of(50), Sort.desc("price"));
* mostExpensive50 = products.namedLike(pattern, Limit.of(50), Sort.desc("price"));
* ...
* secondMostExpensive50 = products.findByNameLike(pattern, Limit.range(51, 100), Sort.desc("price"));
* secondMostExpensive50 = products.namedLike(pattern, Limit.range(51, 100), Sort.desc("price"));
* </pre>
*
* <p>A repository method may not be declared with:
Expand Down
17 changes: 10 additions & 7 deletions api/src/main/java/jakarta/data/page/CursoredPage.java
Original file line number Diff line number Diff line change
Expand Up @@ -55,23 +55,26 @@
* query parameters) of type {@link PageRequest}, for example:</p>
*
* <pre>
* &#64;OrderBy("lastName")
* &#64;OrderBy("firstName")
* &#64;OrderBy("id")
* CursoredPage&lt;Employee&gt; findByHoursWorkedGreaterThan(int hours, PageRequest pageRequest);
* &#64;Find
* &#64;OrderBy(_Employee.LASTNAME)
* &#64;OrderBy(_Employee.FIRSTNAME)
* &#64;OrderBy(_Employee.ID)
* CursoredPage&lt;Employee&gt; withHoursOver(
* &#64;By(_Employee.HOURSWORKED) &#64;Is(GreaterThan.class) 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.withHoursOver(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.withHoursOver(40, page.nextPageRequest());
* </pre>
*
* <p>Here, the instance of {@link PageRequest} returned by
Expand All @@ -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.withHoursOver(40, pageRequest);
* </pre>
*
* <p>By making the query for the next page relative to observed values,
Expand Down
17 changes: 11 additions & 6 deletions api/src/main/java/jakarta/data/page/PageRequest.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,23 +37,28 @@
* regular parameters of the query itself. For example:</p>
*
* <pre>
* &#64;Find
* &#64;OrderBy("age")
* &#64;OrderBy("ssn")
* Page&lt;Person&gt; findByAgeBetween(int minAge, int maxAge, PageRequest pageRequest);
* Page&lt;Person&gt; agedBetween(&#64;By("age") &#64;Is(AtLeast.class) int minAge,
* &#64;By("age") &#64;Is(AtMost.class) int maxAge,
* PageRequest pageRequest);
* </pre>
*
* <p>This method might be called as follows:</p>
*
* <pre>
* Page&lt;Person&gt; page = people.findByAgeBetween(35, 59,
* PageRequest.ofSize(100));
* Page&lt;Person&gt; page = people.agedBetween(
* 35, 59,
* PageRequest.ofSize(100));
* List&lt;Person&gt; results = page.content();
* ...
* while (page.hasNext()) {
* page = people.findByAgeBetween(35, 59,
* page.nextPageRequest().withoutTotal());
* page = people.agedBetween(
* 35, 59,
* page.nextPageRequest().withoutTotal());
* results = page.content();
* ...
* ...
* }
* </pre>
*
Expand Down
7 changes: 6 additions & 1 deletion api/src/main/java/jakarta/data/repository/By.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import jakarta.data.constraint.EqualTo;

/**
* <p>Annotates a parameter of a repository method, specifying a mapping to
Expand All @@ -33,7 +34,11 @@
* to the unique identifier attribute.
* </ul>
* <p>Arguments to the annotated parameter are compared to values of the
* mapped attribute.</p>
* mapped attribute. The {@link EqualTo#value(Object) equality} comparison is
* default. Use the {@link Is#value() @Is} annotation to choose a different
* subtype of {@link jakarta.data.constraint Constraint} to be the comparison.
* </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},
Expand Down
120 changes: 120 additions & 0 deletions api/src/main/java/jakarta/data/repository/Is.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
/*
* 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.constraint.AtLeast;
import jakarta.data.constraint.AtMost;
import jakarta.data.constraint.Constraint;
import jakarta.data.constraint.EqualTo;
import jakarta.data.constraint.GreaterThan;
import jakarta.data.constraint.In;
import jakarta.data.constraint.LessThan;
import jakarta.data.constraint.Like;
import jakarta.data.constraint.NotEqualTo;
import jakarta.data.constraint.NotIn;
import jakarta.data.constraint.NotLike;

/**
* <p>Annotates a parameter of a repository {@link Find} or {@link Delete}
* method, indicating how an entity attribute is compared with the parameter's
* value.</p>
*
* <p>The {@code @Is} annotation's {@link #value()} supplies the type of
* comparison as a subtype of {@link jakarta.data.constraint Constraint}.</p>
*
* <p>The {@link By} annotation must annotate the same parameter to indicate
* the entity attribute name, or otherwise, if the {@code -parameters} compile
* option is enabled, the persistent field is inferred by matching the name of
* the method parameter.</p>
*
* <p>For example,</p>
*
* <pre>
* &#64;Repository
* public interface Products extends CrudRepository&lt;Product, Long&gt; {
*
* // Find Product entities where the price attribute is less than a maximum value.
* &#64;Find
* List&lt;Product&gt; pricedBelow(&#64;By(_Product.PRICE) &#64;Is(LessThan.class) float max);
*
* // Find a page of Product entities where the name field matches a pattern.
* &#64;Find
* Page&lt;Product&gt; search(&#64;By(_Product.NAME) &#64;Is(Like.class) String pattern,
* PageRequest pagination,
* Order&lt;Product&gt; order);
*
* // Remove Product entities with any of the unique identifiers listed.
* &#64;Delete
* void remove(&#64;By(ID) &#64;Is(In.class) List&lt;Long&gt; productIds);
* }
* </pre>
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface Is {

/**
* <p>A subtype of {@link jakarta.data.constraint Constraint} that
* indicates how the entity attribute is compared with a value.</p>
*
* <p>The constraint subtype must have a static method that accepts
* as its only parameter a value compatible with the type (or if primitive,
* a wrapper for the type) of the repository method parameter to which the
* {@code @Is} annotation is applied. The repository method parameter type
* must also be consistent with the respective entity attribute type. This
* list indicates the constraint subtypes that can be used and links to the
* applicable static method for each:</p>
*
* <ul>
* <li>{@link AtLeast#min(Comparable) AtLeast}</li>
* <li>{@link AtMost#max(Comparable) AtMost}</li>
* <li>{@link EqualTo#value(Object) EqualTo}</li>
* <li>{@link GreaterThan#bound(Comparable) GreaterThan}</li>
* <li>{@link In#values(java.util.Collection) In}</li>
* <li>{@link LessThan#bound(Comparable) LessThan}</li>
* <li>{@link Like#pattern(String) Like}</li>
* <li>{@link NotEqualTo#value(Object) NotEqualTo}</li>
* <li>{@link NotIn#values(java.util.Collection) NotIn}</li>
* <li>{@link NotLike#pattern(String) NotLike}</li>
* </ul>
*
* <p>The following example involves a {@code Person} entity that has a
* {@code birthYear} attribute of type {@code int}. It compares the year in
* which a person was born against a minimum and maximum year that are
* supplied as parameters to a repository method:</p>
*
* <pre>
* &#64;Find
* &#64;OrderBy(_Person.BIRTHYEAR)
* List&lt;Person&gt; bornWithin(&#64;By(_Person.BIRTHYEAR) &#64;Is(AtLeast.class) int minYear,
* &#64;By(_Person.BIRTHYEAR) &#64;Is(AtMost.class) int maxYear);
* </pre>
*
* <p>The default constraint is the
* {@linkplain EqualTo#value(Object) equality} comparison.</p>
*
* @return the type of comparison operation.
*/
@SuppressWarnings("rawtypes")
Class<? extends Constraint> value() default EqualTo.class;
}
8 changes: 5 additions & 3 deletions api/src/main/java/jakarta/data/repository/OrderBy.java
Original file line number Diff line number Diff line change
Expand Up @@ -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>
* &#64;OrderBy(value = "price", descending = true)
* {@code Stream<Product>} findByPriceLessThanEqual(double maxPrice);
* &#64;Find
* &#64;OrderBy(value = _Product.PRICE, descending = true)
* {@code Stream<Product>} pricedBelow(&#64;By(_Product.PRICE) &#64;Is(AtMost.class) double maxPrice);
* </pre>
*
* <p>A repository method with an {@code @OrderBy} annotation must not
Expand Down Expand Up @@ -116,8 +117,9 @@
* <p>For example,</p>
*
* <pre>
* &#64;Find
* &#64;OrderBy("age")
* Stream&lt;Person&gt; findByLastName(String lastName);
* Stream&lt;Person&gt; withLastName(&#64;By("lastName") String surname);
* </pre>
*
* @return entity attribute name.
Expand Down
5 changes: 3 additions & 2 deletions api/src/main/java/jakarta/data/repository/Repository.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,9 @@
* &#64;Repository
* public interface Products extends DataRepository&lt;Product, Long&gt; {
*
* &#64;Find
* &#64;OrderBy("price")
* List&lt;Product&gt; findByNameLike(String namePattern);
* List&lt;Product&gt; namedLike(&#64;By("name") &#64;Is(Like.class) String namePattern);
*
* &#64;Query("UPDATE Product SET price = price - (price * ?1) WHERE price * ?1 &lt;= ?2")
* int putOnSale(float rateOfDiscount, float maxDiscount);
Expand All @@ -53,7 +54,7 @@
* Products products;
*
* ...
* found = products.findByNameLike("%Printer%");
* found = products.namedLike("%Printer%");
* numUpdated = products.putOnSale(0.15f, 20.0f);
* </pre>
*
Expand Down
Loading