Specification: Jakarta Data Version: 1.0 Status: FINAL Release: September 30, 2024
Copyright
Copyright (c) 2022, 2024 Eclipse Foundation.
Eclipse Foundation Specification License - v1.1
By using and/or copying this document, or the Eclipse Foundation document from which this statement is linked or incorporated by reference, you (the licensee) agree that you have read, understood, and will comply with the following terms and conditions:
Permission to copy, and distribute the contents of this document, or the Eclipse Foundation document from which this statement is linked, in any medium for any purpose and without fee or royalty is hereby granted, provided that you include the following on ALL copies of the document, or portions thereof, that you use:
-
link or URL to the original Eclipse Foundation document.
-
All existing copyright notices, or if one does not exist, a notice (hypertext is preferred, but a textual representation is permitted) of the form: "Copyright (c) [$date-of-document] Eclipse Foundation AISBL https://www.eclipse.org/legal/efsl.php "
Inclusion of the full text of this NOTICE must be provided. We request that authorship attribution be provided in any software, documents, or other items or products that you create pursuant to the implementation of the contents of this document, or any portion thereof.
No right to create modifications or derivatives of Eclipse Foundation documents is granted pursuant to this license, except anyone may prepare and distribute derivative works and portions of this document in software that implements the specification, in supporting materials accompanying such software, and in documentation of such software, PROVIDED that all such works include the notice below. HOWEVER, the publication of derivative works of this document for use as a technical specification is expressly prohibited.
The notice is:
"Copyright (c) 2022, 2024 Eclipse Foundation AISBL.This software or document includes material copied from or derived from Jakarta Data and https://jakarta.ee/specifications/data/."
Disclaimers
THIS DOCUMENT IS PROVIDED "AS IS," AND TO THE EXTENT PERMITTED BY APPLICABLE LAW THE COPYRIGHT HOLDERS AND THE ECLIPSE FOUNDATION AISBL MAKE NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, OR TITLE; THAT THE CONTENTS OF THE DOCUMENT ARE SUITABLE FOR ANY PURPOSE; NOR THAT THE IMPLEMENTATION OF SUCH CONTENTS WILL NOT INFRINGE ANY THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS.
TO THE EXTENT PERMITTED BY APPLICABLE LAW THE COPYRIGHT HOLDERS AND THE ECLIPSE FOUNDATION AISBL WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE DOCUMENT OR THE PERFORMANCE OR IMPLEMENTATION OF THE CONTENTS THEREOF.
The name and trademarks of the copyright holders or the Eclipse Foundation AISBL may NOT be used in advertising or publicity pertaining to this document or its contents without specific, written prior permission. Title to copyright in this document will at all times remain with copyright holders.
1. Introduction
Query by Method Name is a query language suitable for embedding in the names of methods written in Java. As such, its syntax is limited to the use of legal identifier characters, so the text of a query must contain neither whitespace, nor punctuation characters, nor numeric operators, nor comparison operators.
Jakarta Data 1.0 offers a Query by Method Name facility as an extension to the specification, providing a migration path for existing applications written for repository frameworks which offer similar functionality.
@Repository
public interface ProductRepository extends BasicRepository<Product, Long> {
List<Product> findByName(String name);
@OrderBy("price")
List<Product> findByNameLike(String namePattern);
List<Product> findByNameLikeAndPriceLessThanOrderByPriceDesc(String namePattern, float priceBelow);
}
The functionality described here overlaps significantly with both:
-
parameter-based automatic query methods, that is,
@Find
and@Delete
, and -
annotated query methods, that is
@Query
and Jakarta Data Query Language or Jakarta Persistence Query Language.
Therefore, these alternative approaches are strongly preferred for newly-written code.
A Jakarta Data provider is required to support the Query by Method Name extension in Jakarta Data 1.0.
A Jakarta Data provider backed by a key-value or wide-column datastore is not required to support Query by Method Name. |
This functionality is considered deprecated, and the requirement that a provider support the Query by Method Name extension will be removed in a future version of Jakarta Data. |
2. Query by Method Name
In Query by Method Name, a query is expressed via a set of method naming conventions.
A method name is formed by concatenating, in the following order:
-
an action, which must be
find
,delete
,count
, orexists
, -
an optional limit,
-
a optional restriction, and
-
an optional order.
2.1. Limits
A find
query may have a limit, for example, First
or First10
. Other actions must not be combined with a limit.
The limit determines the maximum number of records which may be returned by the query. If the query has an order, then the records which are returned are those which occur first after sorting.
2.2. Restrictions
A restriction specifies the criteria used to filter records. It is formed by concatenating By
with one or more conditions, delimited by And
or Or
, for example, PriceLessThanAndNameLike
.
Each condition is formed by concatenating, in the following order:
-
a property name, which may be a compound name, as specified below in Persistent Field Names in Query by Method Name,
-
optionally,
IgnoreCase
(for text properties), -
optionally,
Not
, -
optionally, an operator such as
LessThan
orLike
.
Absence of an operator implies the equality condition.
The conditions belonging to the restriction determine the parameters of the method, as specified below in Query by Method Name Conditions.
2.3. Orders
A find
query may have an order. The order specifies how records must be sorted. It is formed by concatenating OrderBy
with one or more ordered pairs of an entity attribute name and a direction of sorting, Asc
or Desc
. The direction may be omitted if there is only one property, in which case Asc
is implied.
The order is lexicographic, that is, ordered pairs occurring earlier take precedence. An ordered pair occurring later is only used to resolve "ties" between records which cannot be unambiguously ordered using only earlier ordered pairs.
If no order is specified, the records are not sorted.
2.4. Example query methods
The following table displays some examples of legal method signatures.
|
Find entities by the |
|
Find entities where |
|
Find entities by the |
|
Find entities by |
|
Find entities by matching the |
2.5. BNF Grammar for Query Methods
The rules for parsing an interpreting a method name are specified by the following grammar.
query : find | action
find : "find" limit? ignoredText? restriction? order?
action : ("delete" | "count" | "exists") ignoredText? restriction?
restriction : "By" predicate
limit : "First" max?
predicate : condition (("And" | "Or") condition)*
condition : property "IgnoreCase"? "Not"? operator?
operator
: "Contains"
| "EndsWith"
| "StartsWith"
| "LessThan"
| "LessThanEqual"
| "GreaterThan"
| "GreaterThanEqual"
| "Between"
| "Like"
| "In"
| "Null"
| "True"
| "False"
property : identifier ("_" identifier)*
identifier : word
max : digit+
order : "OrderBy" (property | orderItem+)
orderItem : property ("Asc" | "Desc")
Quoted names are considered case-sensitive keywords.
Rule name | Explanation |
---|---|
|
May be a |
|
A |
|
Any other kind of operation has only a restriction to a subset of records. |
|
Restricts the records returned to those which satisfy a predicate |
|
Limits the records retrieved by a |
|
Optional text that does not contain |
|
A filtering criteria, which may include multiple conditions separated by |
|
A property of the queried entity and an operator. |
|
An operator belonging to a condition, for example, |
|
A property name, which can include underscores for nested properties. |
|
A legal Java identifier, not containing an underscore. |
|
A positive whole number. |
|
Specifies that results of a |
|
A field used to sort results, where |
2.6. Query by Method Name Keywords
An implementation of Query by Method Name must support the following types of operation.
Action | Description |
---|---|
|
Returns entity instances representing the records which satisfy the restriction, or representing all records if there is no restriction. |
|
Deletes every record which satisfy the restriction, or all records if there is no restriction, and returns either no result ( |
|
Returns the number of records which satisfy the restriction, or the total number of records if there is no restriction. |
|
Returns |
An implementation of Query by Method Name must support the following keywords.
Keyword | Description | Method signature example |
---|---|---|
|
The |
|
|
The |
|
|
Negates the condition that immediately follows the |
|
|
For a query with ordered results, limits the quantity of results to the number following First, or if there is no subsequent number, to a single result. |
|
|
Specify a static sorting order followed by one or more ordered pairings of a property path and direction ( |
|
|
Specify a static sorting order of descending. |
|
|
Specify a static sorting order of ascending. |
|
For relational databases, the logical operator And
takes precedence over Or
, meaning that And
is evaluated on conditions before Or
when both are specified on the same method. For other database types, the precedence is limited to the capabilities of the database. For example, some graph databases are limited to precedence in traversal order.
An implementation of Query by Method Name backed by a document or graph database is not required to support the First keyword. A repository method must raise java.lang.UnsupportedOperationException or a more specific subclass of the exception if the database does not support this functionality.
|
2.7. Query by Method Name Conditions
In addition to equality conditions, Query by Method Name defines the following kinds of condition.
Keyword | Property type | Parameters | Description | Method signature example |
---|---|---|---|---|
|
Any sortable type |
2 |
Find results where the property is between (inclusive of) two given values, with the first value being the inclusive minimum and the second value being the inclusive maximum. |
|
|
|
1 |
Matches string values with the given substring, which can be a pattern. |
|
|
|
1 |
Matches String values with the given ending, which can be a pattern. |
|
|
Any sortable type |
1 |
Find results where the property is less than the given value |
|
|
Any sortable type |
1 |
Find results where the property is greater than the given value |
|
|
Any sortable type |
1 |
Find results where the property is less than or equal to the given value |
|
|
Any sortable type |
1 |
Find results where the property is greater than or equal to the given value |
|
|
|
1 |
Matches string values against the given pattern. |
|
|
|
Requests that string values be compared independent of case for query conditions and ordering. |
|
|
|
Any type |
1 |
Find results where the property is one of the values that are contained within the given |
|
|
Any type |
0 |
Finds results where the property has a null value. |
|
|
|
1 |
Matches String values with the given beginning, which can be a pattern. |
|
|
|
0 |
Finds results where the property has a boolean value of |
|
|
|
0 |
Finds results where the property has a boolean value of |
|
Most Query by Method Name conditions require a single repository method parameter. The Between
condition requires two parameters. Null
, True
, and False
require none. An In
condition requires a parameter of type Set<T>
where T
is the type of the property. The repository method parameters used for Query by Method Name conditions follow the order in which the Query by Method Name conditions appear within the method name.
Wildcard characters for patterns are determined by the data store. For relational databases, _
matches any one character and %
matches zero or more characters.
An implementation of Query by Method Name backed by a document or graph database is not required to support Contains , EndsWith , StartsWith , Like , IgnoreCase , In , or Null . A repository method must raise java.lang.UnsupportedOperationException or a more specific subclass of the exception if the database does not provide the requested functionality.
|
In the following example the value of the first parameter, namePattern
, is used for NameLike
, the values of the second and third parameters, minYear
and maxYear
, are used for YearMadeBetween
, and the value of the fourth parameter, maxPrice
, is used for PriceLessThan
.
List<Product> findByNameLikeAndYearMadeBetweenAndPriceLessThan(String namePattern,
int minYear,
int maxYear,
float maxPrice,
Limit limit,
Order<Product> sortBy)
2.8. Return Types
The return type of a Query by Method Name is determined as indicated in the following table, where E
is the queried entity type.
Operation | Return type | Notes |
---|---|---|
|
|
|
|
|
|
|
|
|
|
|
For queries returning a single item (or none) |
|
|
For queries where it is possible to return more than one item |
|
|
The caller must call |
|
|
For use with pagination |
2.9. Persistent Field Names in Query by Method Name
Section 3.2 of the Jakarta Data specification describes how names are assigned to persistent fields of an entity.
For Query by Method Name, the use of delimiters within a compound name is optional. Delimiters may be omitted entirely from a compound name when they are not needed to disambiguate the persistent field to which the name refers. But for a given entity property name, delimiter usage must be consistent: either the delimiter must be used between every pair of persistent field names within the compound name, or it must not occur within the compound name.
Resolution of a persistent field involves the following steps:
-
A persistent field name is extracted from the method name according to the BNF Grammar for Query Methods. For example, if the query method name is
findByAddressZipCode
, the extracted field name isAddressZipCode
. -
The extracted name is matched against the fields of the entity class. If the name assigned to a persistent field of the entity class matches the extracted name, ignoring case, then the extracted name resolves to that field.
-
Otherwise, if no match is found among the fields of the entity, the extracted name is matched against the fields of entity classes and embedded classes reachable from the entity class, interpreting the extracted name as a compound name, as outlined in the previous section, both with and without the optional delimiter. If the compound name assigned to a persistent field matches the extracted name, also interpreted as a compound name, and ignoring case, then the extracted name resolves to that field.
-
If no matching persistent field is found in either of the previous steps, the provider is permitted to reject the query method or to throw
UnsupportedOperationException
when the method is called.
A persistent field name used in a Query by Method Name must not contain a keyword reserved by the grammar.
2.9.1. Scenario 1: Person Repository with Unambiguous Resolution
In this scenario, we have the following data model:
class Person {
private Long id;
private MailingAddress address;
}
class MailingAddress {
private int zipcode;
}
The Person
entity does not have an addressZipCode
field, so use of the delimiter is optional. It is valid to write both of the following repository methods, which have the same meaning,
List<Person> findByAddressZipCode(int zipCode);
List<Person> findByAddress_zipcode(int zipCode);
2.9.2. Scenario 2: Customer Repository with Resolution that requires a Delimiter
In this scenario, we have the following data model:
class Customer {
private Long id;
private String addressZipCode;
private MailingAddress address;
}
class MailingAddress {
private int zipcode;
}
The Customer
entity has an addressZipCode
field, as well as an address
field for an embeddable class with a zipcode
field. The method name findByAddressZipCode
points to the addressZipCode
field and cannot be used to navigate to the embedded class. To navigate to the zipcode
field of the embedded class, the delimiter must be used:
List<Customer> findByAddress_zipcode(int zipCode);