From abf00cd8a16ea48d274ebd2cfd70216ffbed427f Mon Sep 17 00:00:00 2001 From: Gavin King Date: Sun, 3 Mar 2024 01:13:14 +0100 Subject: [PATCH 01/21] Draft specification of JDQL --- spec/src/antlr/JDQL.g4 | 92 +++++ spec/src/main/asciidoc/jakarta-data.adoc | 1 + .../src/main/asciidoc/query-language.asciidoc | 337 ++++++++++++++++++ spec/src/main/asciidoc/repository.asciidoc | 9 +- 4 files changed, 438 insertions(+), 1 deletion(-) create mode 100644 spec/src/antlr/JDQL.g4 create mode 100644 spec/src/main/asciidoc/query-language.asciidoc diff --git a/spec/src/antlr/JDQL.g4 b/spec/src/antlr/JDQL.g4 new file mode 100644 index 000000000..7b98f15b9 --- /dev/null +++ b/spec/src/antlr/JDQL.g4 @@ -0,0 +1,92 @@ +grammar JDQL; + +statement : select_statement | update_statement | delete_statement; + +select_statement : select_clause? from_clause? where_clause? orderby_clause?; +update_statement : 'UPDATE' entity_name set_clause where_clause?; +delete_statement : 'DELETE' from_clause where_clause?; + +from_clause : 'FROM' entity_name; + +where_clause : 'WHERE' conditional_expression; + +set_clause : 'SET' update_item (',' update_item)*; +update_item : state_field_path_expression '=' (scalar_expression | 'NULL'); + +select_clause : 'SELECT' select_list; +select_list + : scalar_expression (',' scalar_expression)* // could limit to single select item and/or to state_field_path_expression + | aggregate_expression + ; +aggregate_expression : 'COUNT' '(' '*' ')'; + +orderby_clause : 'ORDER' 'BY' orderby_item (',' orderby_item)*; +orderby_item : state_field_path_expression ('ASC' | 'DESC'); + +conditional_expression + // highest to lowest precedence + : '(' conditional_expression ')' + | null_comparison_expression + | in_expression + | between_expression + | like_expression + | comparison_expression + | 'NOT' conditional_expression + | conditional_expression 'AND' conditional_expression + | conditional_expression 'OR' conditional_expression + ; + +comparison_expression : scalar_expression ('=' | '>' | '>=' | '<' | '<=' | '<>') scalar_expression; +between_expression : scalar_expression 'NOT'? 'BETWEEN' scalar_expression 'AND' scalar_expression; +like_expression : scalar_expression 'NOT'? 'LIKE' STRING; + +in_expression : state_field_path_expression 'NOT'? 'IN' '(' in_item (',' in_item)* ')'; +in_item : literal | enum_literal | input_parameter; // could simplify to just literal + +null_comparison_expression : state_field_path_expression 'IS' 'NOT'? 'NULL'; + +scalar_expression + // highest to lowest precedence + : '(' scalar_expression ')' + | primary_expression + | ('+' | '-') scalar_expression + | scalar_expression ('*' | '/') scalar_expression + | scalar_expression ('+' | '-') scalar_expression + | scalar_expression '||' scalar_expression + ; + +primary_expression + : function_expression + | special_expression + | state_field_path_expression + | enum_literal + | input_parameter + | literal + ; + +function_expression + : 'ABS' '(' scalar_expression ')' + | 'LENGTH' '(' scalar_expression ')' + | 'LOWER' '(' scalar_expression ')' + | 'UPPER' '(' scalar_expression ')' + | 'LEFT' '(' scalar_expression ',' scalar_expression ')' + | 'RIGHT' '(' scalar_expression ',' scalar_expression ')' + ; + +special_expression + : 'LOCAL' 'DATE' + | 'LOCAL' 'DATETIME' + | 'TRUE' + | 'FALSE' + ; + +state_field_path_expression : IDENTIFIER ('.' IDENTIFIER)*; + +entity_name : IDENTIFIER; // no ambiguity + +enum_literal : IDENTIFIER; // ambiguity with state_field_path_expression resolvable semantically + +input_parameter : ':' IDENTIFIER | '?' INTEGER; + +literal : STRING | INTEGER | DOUBLE; + diff --git a/spec/src/main/asciidoc/jakarta-data.adoc b/spec/src/main/asciidoc/jakarta-data.adoc index e21b3b5c8..96f0ab20b 100644 --- a/spec/src/main/asciidoc/jakarta-data.adoc +++ b/spec/src/main/asciidoc/jakarta-data.adoc @@ -44,5 +44,6 @@ include::chapters/license/license-efsl.adoc[] include::chapters/introduction/introduction.asciidoc[] include::repository.asciidoc[] +include::query-language.asciidoc[] include::jakarta-ee.adoc[] include::portability.asciidoc[] diff --git a/spec/src/main/asciidoc/query-language.asciidoc b/spec/src/main/asciidoc/query-language.asciidoc new file mode 100644 index 000000000..4c2032718 --- /dev/null +++ b/spec/src/main/asciidoc/query-language.asciidoc @@ -0,0 +1,337 @@ +== Jakarta Data Query Language + +The Jakarta Data Query Language (JDQL) is a simple language designed to be used inside the `@Query` annotation to specify the semantics of query methods of Jakarta Data repositories. The language is in essence a subset of the widely-used Jakarta Persistence Query Language (JPQL), and thus a dialect of SQL. But, consistent with the goals of Jakarta Data, it is sufficiently limited in functionality that it is easily implementable across a wide variety of data storage technologies. Thus, the language defined in this chapter excludes features of JPQL which, while useful when the target datasource is a relational database, cannot be easily implemented on all non-relational datastores. In particular, the `from` clause of a Jakarta Data query may contain only a single entity. + +NOTE: A Jakarta Data provider backed by access to a relational database might choose to allow the use of a much larger subset of JPQL—or even the whole language—via the `@Query` annotation. Such extensions are not required by this specification. + +=== Type system + +Every expression in a JDQL query is assigned a Java type. An implementation of JDQL is required to support the Java types listed in <>, that is: primitive types, `String`, `LocalDate`, `LocalDateTime`, `LocalTime`, and `Instant`, `java.util.UUID`, `java.math.BigInteger` and `java.math.BigDecimal`, `byte[]`, and user-defined `enum` types. + +NOTE: An implementation of JDQL is permitted and encouraged to support additional types. Use of such types is not guaranteed to be portable between implementations. + +The interpretation of an operator expression or literal expression of a given type is given by the interpretation of the equivalent expression in Java. However, the precise behavior of some queries might vary depending on the native semantics of queries on the underlying datastore. For example, numeric precision and overflow, string collation,and integer division are permitted to depart from the semantics of the Java language. + +NOTE: This specification should not be interpreted to mandate an inefficient implementation of query language constructs in cases where the native behavior of the database varies from Java in such minor ways. That said, portability between Jakarta Data providers is maximized when their behavior is closest to the Java language. + +=== Lexical structure + +Lexical analysis requires recognition of the following token types: + +- keywords (reserved identifiers), +- regular identifiers, +- named and ordinal parameters, +- operators and punctuation characters, +- literal strings, and +- integer and decimal number literals. + +==== Identifiers and keywords + +An _identifier_ is any legal Java identifier which is not a keyword. Identifiers are case-sensitive: `hello`, `Hello`, and `HELLO` are distinct identifiers. + +In the JDQL grammar, identifiers are labelled with the `IDENTIFIER` token type. + +The following identifiers are _keywords_: `select`, `update`, `set`, `delete`, `from`, `where`, `order`, `by`, `asc`, `desc`, `not`, `and`, `or`, `between`, `like`, `in`, `null`, `local`, `true`, `false`. In addition, every reserved identifier listed in section 4.4.1 of the Jakarta Persistence specification version 3.2 is also considered a reserved identifier. Keywords and other reserved identifiers are case-insensitive: `null`, `Null`, and `NULL` are three ways to write the same keyword. + +NOTE: Use of a reserved identifier as a regular identifier in JDQL might be accepted by a given Jakarta Data provider, but such usage is not guaranteed to be portable between providers. + +==== Parameters + +A _named parameter_ is a legal Java identifier prefixed with the `:` character, for example, `:name`. + +An _ordinal parameter_ is a decimal integer prefixed with the `?` character, for example, `?1`. + +==== Operators and punctuation + +The character sequences `+`,`-`,`*`,`/`,`||`,`=`,`<`,`>`,`<>`,`<=`,`>=` are _operators_. + +The characters `(`,`)`, and `,` are _punctuation characters_. + +==== String literals + +A _literal string_ is a character sequence quoted using the character `'`. + +A single literal `'` character may be included within a string literal by self-escaping it, that is, by writing `''`. For example, the string literal ``'Furry''s theorem has nothing to do with furries.'`` evaluates to the string `pass:[Furry's theorem has nothing to do with furries.]`. + +In the grammar, literal strings are labelled with the `STRING` token type. + +==== Numeric literals + +Numeric literals come in two flavors: + +- any legal Java decimal literal of type `int` or `long` is an _integer literal_, and +- any legal Java literal of type `float` or `double` is a _decimal literal_. + +In the grammar, integer and decimal literals are labelled with the `INTEGER` and `DOUBLE` token types respectively. + +NOTE: JDQL does not require support for literals written octal or hexadecimal. + +==== Whitespace + +The characters Space, Horizontal Tab, Line Feed, Form Feed, and Carriage Return are considered whitespace characters and make no contribution to the token stream. + +As usual, token recognition is "greedy". Therefore, whitespace must be placed between two tokens when: + +- a keyword directly follows an identifier or named parameter, +- an identifier directly follows a keyword or named parameter, or +- a numeric literal directly follows an identifier, keyword, or parameter. + +=== Expressions + +An expression is a sequence of tokens to which a Java type can be assigned, and which evaluates to a well-defined value when the query is executed. In JDQL, expressions may be categorized as: + +- literals, +- special values, +- parameters, +- enum literals, +- paths, +- function calls, and +- operator expressions. + +==== Literal expressions + +A string, integer, or decimal literal is assigned the type it would be assigned in Java. So, for example, `'Hello'` is assigned the type `java.lang.String`, `123` is assigned the type `int`, `1e4` is assigned the type `double`, and `1.23f` is assigned the type `float`. + +The syntax for literal expressions is given by the `literal` grammar rule, and in the previous section titled <>. + +When executed, a literal expression evaluates to its literal value. + +==== Special values + +The special values `true` and `false` are assigned the type `boolean`, and evaluate to their literal values. + +The special values `local date` and `local datetime` are assigned the types `java.time.LocalDate` and `java.time.LocalDateTime`, and evaluate to the current date and current datetime of the database server, respectively. + +The syntax for special values is given by the `special_expression` grammar rule. + +==== Parameter expressions + +A parameter expression, with syntax given by `input_parameter`, is assigned the type of the repository method parameter it matches. For example, the parameter `:titlePattern` is assigned the type `java.lang.String`: + +[source,java] +---- +@Query("where title like :titlePattern") +List booksMatchingTitle(String titlePattern); +---- + +When executed, a parameter expression evaluates to the argument supplied to the parameter of the repository method. + +==== Enum literals + +An _enum literal expression_ is a Java identifier, with syntax specified by `enum_literal`, and may only occur as the right operand of a `set` assignment or `=`/`<>` equality comparison. It is assigned the type of the left operand of the assignment or comparison. The type must be a Java `enum` type, and the identifier must be the name of an enumerated value of the `enum` type. For example, `day <> MONDAY` is a legal comparison expression. + +When executed, an enum expression evaluates to the named member of the Java `enum` type. + +==== Path expressions + +A _path expression_ is a period-separated list of Java identifiers, with syntax specified by `state_field_path_expression`. Each identifier is interpreted as the name of a field of an entity or embeddable class. Each prefix of the list is assigned a Java type: + +- the first element of the list is assigned the type of the named field of the entity being queried, and +- each subsequent element is assigned the type of the named field of the type assigned to the previous element. + +The type of the whole path expression is the type of the last element of the list. For example, `pages` is assigned the type `int`, `address` is assigned the type `org.example.Address`, and `address.street` is assigned the type `java.lang.String`. + +When executed, a path expression is evaluated in the context of a given record of the queried entity type, and evaluates to the value of the entity field for the given record. + +==== Function calls + +A _function call_ is the name of a JDQL function, followed by a parenthesized list of argument expressions, with syntax given by `function_expression`. + +- The `abs()` function is assigned the type of its numeric argument, and evaluates to the absolute value of the numeric value to which its argument evaluates. Its argument must be of numeric type. + +- The `length()` function is assigned the type `java.lang.Integer`, and evaluates to the length of string to which its argument evaluates. Its argument must be of type `java.lang.String`. + +- The `lower()` function is assigned the type `java.lang.String`, and evaluates to the lowercase form of the string to which its argument evaluates. Its argument must be of type `java.lang.String`. + +- The `upper()` function is assigned the type `java.lang.String`, and evaluates to the uppercase form of the string to which its argument evaluates. Its argument must be of type `java.lang.String`. + +- The `left()` function is assigned the type `java.lang.String`, and evaluates to a prefix of the string to which its first argument evaluates. The length of the prefix is given by the integer value to which its second argument evaluates. The first argument must be of type `java.lang.String`, and the second argument must be of integral numeric type. + +- The `right()` function is assigned the type `java.lang.String`, and evaluates to a suffix of the string to which its first argument evaluates. The length of the suffix is given by the integer value to which its second argument evaluates. The first argument must be of type `java.lang.String`, and the second argument must be of integral numeric type. + +When any argument expression of any function call evaluates to a null value, the whole function call evaluates to null. + +==== Operator expressions + +The syntax of an _operator expression_ is given by the `scalar_expression` rule. Within an operator expression, parentheses indicate grouping. + +The concatenation operator `||` is assigned the type `java.lang.String`. Its operand expressions must also be of type `java.lang.String`. When executed, a concatenation operator expression evaluates to a new string concatenating the strings to which its arguments evaluate. + +The numeric operators `+`, `-`, `*`, and `/` have the same meaning for primitive numeric types they have in Java, and operator expression involving these operators are assigned the types they would be assigned in Java. + +NOTE: As an exception, when the operands of `/` are both integers, a JDQL implementation is not required to interpret the operator expression as integer division if that is not the native semantics of the database. However, portability is maximized when Jakarta Data providers _do_ interpret such an expression as integer division. + +The four numeric operators may also be applied to an operand of wrapper type, for example, to `java.lang.Integer` or `java.lang.Double`. In this case, the operator expression is assigned a wrapper type, and evaluates to a null value when either of its operands evaluates to a null value. When both operands are non-null, the semantics are identical to the semantics of an operator expression involving the corresponding primitive types. + +The four numeric operators may also be applied to operands of type `java.math.BigInteger` or `java.math.BigDecimal`. + +A numeric operator expression is evaluated according to the native semantics of the database. In translating an operator expression to the native query language of the database, a Jakarta Data provider is encouraged, but not required, to apply reasonable transformations so that evaluation of the expression more closely mimics the semantics of the Java language. + +==== Numeric types and numeric type promotion + +The type assigned to an operator expression depends on the types of its operand expression, which need not be identical. The rules for numeric promotion are given in section 4.7 of the Jakarta Persistence specification version 3.2: + +> * If there is an operand of type `Double` or `double`, the expression is of type `Double`; +> * otherwise, if there is an operand of type `Float` or `float`, the expression is of type `Float`; +> * otherwise, if there is an operand of type `BigDecimal`, the expression is of type `BigDecimal`; +> * otherwise, if there is an operand of type `BigInteger`, the expression is of type `BigInteger`, unless the operator is `/` (division), in which case the expression type is not defined here; +> * otherwise, if there is an operand of type `Long` or `long`, the expression is of type `Long`, unless the operator is `/` (division), in which case the expression type is not defined here; +> * otherwise, if there is an operand of integral type, the expression is of type `Integer`, unless the operator is `/` (division), in which case the expression type is not defined here. + +=== Conditional expressions + +A _conditional expression_ is a sequence of tokens which specifies a condition which, for a given record, might be _satisfied_ or _unsatisfied_. Unlike the scalar <> defined in the previous section, a conditional expression is not considered to have a well-defined type. + +NOTE: JPQL defines the result of a conditional expression in terms of ternary logic. JDQL does not specify that a conditional expression evaluates to well-defined value, only the effect of the conditional expression when it is used as a restriction. The "value" of a conditional expression is not considered observable by the application program. + +Conditional expressions may be categorized as: + +- `null` comparisons, +- `in` expressions, +- `between` expressions, +- `like` expressions, +- equality and inequality operator expressions, and +- logical operator expressions. + +The syntax for conditional expressions is given by the `conditional_expression` rule. Within a conditional expression, parentheses indicate grouping. + +==== Null comparisons + +A `null` comparison, with syntax given by `null_comparison_expression` is satisfied when: + +- the `not` keyword is missing, and its operand evaluates to a null value, or +- the `not` keyword occurs, and its operand evaluates to any non-null value. + +==== In expressions + +An `in` expression, with syntax given by `in_expression` is satisfied when its leftmost operand evaluates to a non-null value, and: + +- the `not` keyword is missing, and any one of its parenthesized operands evaluates to the same value as its leftmost operand, or +- the `not` keyword occurs, and none of its parenthesized operands evaluate to the same value as its leftmost operand. + +All operands must have the same type. + +==== Between expressions + +A `between` expression, with syntax given by `between_expression` is satisfied when its operands all evaluate to non-null values, and, if the `not` keyword is missing, its left operand evaluates to a value which is: + +- larger than or equal to the value take by its middle operand, and +- smaller than or equal to the value taken by its right operand. + +Or, if the `not` keyword occurs, the left operand must evaluate to a value which is: + +- strictly smaller than to the value take by its middle operand, or +- strictly larger than the value taken by its right operand. + +All three operands must have the same type. + +==== Like expressions + +A `like` expression is satisfied when its left operand evaluates to a non-null value and: + +- the `not` keyword is missing, and this value matches the pattern, or +- the `not` keyword occurs, and the value does not match the pattern. + +The left operand must have type `java.lang.String`. + +Within the pattern, `_` matches any single character, and `%` matches any sequence of characters. + +==== Equality and inequality operators + +The equality and inequality operators are `=`, `<>`, `<`, `>`, `<=`, `>=`. + +- For primitive types, these operators have the same meaning they have in Java, except for `<>` which has the same meaning that `!=` has in Java. Such an operator expression is satisfied when the equivalent operator expression would evaluate to `true` in Java. +- For wrapper types, these operators are satisfied if both operands evaluate to non-null values, and the equivalent operator expression involving primitives would be satisfied. +- For other types, these operators are evaluated according to the native semantics of the database. + +NOTE: Portability is maximized when Jakarta Data providers interpret equality and inequality operators in a manner consistent with the implementation of `Object.equals()` or `Comparable.compareTo()` for the assigned Java type. + +NOTE: For string values, a database might have a different collation algorithm to Java. In evaluating an inequality involving string operands, an implementation of JDQL is not required to emulate Java collation. + +The operands of an equality or inequality operator must have the same type. + +==== Logical operators + +The logical operators are `and`, `or`, and `not`. + +- An `and` operator expression is satisfied if and only if both its operands are satisfied. +- An `or` operator expression is satisfied if and only if at least one of its operands are satisfied. +- A `not` operator expression is never satisfied if its operand _is_ satisfied. + +This specification leaves undefined the interpretation of the `not` operator when its operand _is not_ satisfied. + +CAUTION: A compliant implementation of JDQL might feature SQL/JPQL-style ternary logic, where `not n > 0` is an unsatisfied logical expression when `n` evaluates to null, or it might feature binary logic where the same expression is considered satisfied. Application programmers should take great care when using the `not` operator with scalar expressions involving `null` values. + +=== Clauses + +Each JDQL statement is built from a sequence of _clauses_. The beginning of a clause is identified by a keyword: `from`, `where`, `select`, `set`, or `order`. + +There is a logical ordering of clauses, reflecting the order in which their effect must be computed by the datastore: + +1. `from` +2. `where`, +3. `select` or `set`, +4. `order`. + +The interpretation and effect of each clause in this list is influenced by clauses occurring earlier in the list, but not by clauses occurring later in the list. + +==== From clause + +The `from` clause, with syntax given by `from_clause`, specifies an _entity name_ which identifies the queried entity. Path expressions occurring in later clauses are interpreted with respect to this entity. That is, the first element of each path expression in the query must be a persistent field of the entity named in the `from` clause. The entity name is a Java identifier, usually the unqualified name of the entity class, as specified in <>. + +The `from` clause is optional in `select` statements. When it is missing, the queried entity is determined by the return type of the repository method, or, if the return type is not an entity type, by the primary entity type of the repository. + +NOTE: The syntax of the `update` statement is irregular, with the `from` keyword implied. That is, the syntax _should_ be `update from Entity`, but for historical reasons it is simply `update Entity`. + +==== Where clause + +The `where` clause, with syntax given by `where_clause`, specifies a conditional expression used to restrict the records returned, deleted, or updated by the query. Only records for which the conditional expression is satisfied are returned, deleted, or updated. + +The `where` clause is always optional. When it is missing, there is no restriction, and, and all records are of the queried entity type are returned, deleted, or updated. + +==== Select clause + +The `select` clause, with syntax given by `select_clause`, specifies a list of expressions which are returned by the query. Each expression in the list is evaluated for each record which satisfies the restriction imposed by the `where` clause. Alternatively, the `select` clause may contain a single `count(*)` aggregate expression, which evaluates to the number of records which satisfy the restriction. + +The `select` clause is optional in `select` statements. When it is missing, the query returns the queried entity. + +==== Set clause + +The `set` clause, with syntax given by `set_clause`, specifies a list of updates to fields of the queried entity. For each record which satisfies the restriction imposed by the `where` clause, and for each element of the list, the scalar expression is evaluated and assigned to the entity field identified by the path expression. + +==== Order clause + +The `order` clause (or `order by` clause), with syntax given by `orderby_clause`, specifies a lexicographic order for the query results, that is, a list of entity fields used to sort the records which satisfy the restriction imposed by the `where` clause. The keywords `asc` and `desc` specify that a given field should be sorted in ascending or descending order respectively; when neither is specified, ascending order is the default. + +The `order` clause is always optional. When it is missing, and when no sort criteria are given as arguments to a parameter of the repository method, the order of the query results is undefined, and may not be deterministic. + +=== Statements + +Finally, there are three kinds of _statement_: + +- `select` statements, +- `update` statements, and +- `delete` statements. + +The clauses which can appear in a statement are given by the grammar for each kind of statement. + +==== Select statements + +A `select` statement, with syntax given by `select_statement`, returns data to the client. For each record which satisfies the restriction imposed by the `where` clause, a result is returned containing the values obtained by evaluating the scalar expressions in the `select` clause. Alternatively, for the case of `select count(*)`, the query returns the number of records which satisfied the restriction. + +==== Update statements + +An `update` statement, with syntax given by `update_statement`, updates each record which satisfies the restriction imposed by the `where` clause, and returns the number of updated records to the client. + +==== Delete statements + +A `delete` statement, with syntax given by `delete_statement`, deletes each record which satisfies the restriction imposed by the `where` clause, and returns the number of deleted records to the client. + +=== Syntax + +The following grammar defines the syntax of JDQL, via ANTLR4-style BNF. + +[source, antlrv4] +---- +include::../../antlr/JDQL.g4[] +---- diff --git a/spec/src/main/asciidoc/repository.asciidoc b/spec/src/main/asciidoc/repository.asciidoc index d39cee408..49789621e 100644 --- a/spec/src/main/asciidoc/repository.asciidoc +++ b/spec/src/main/asciidoc/repository.asciidoc @@ -460,6 +460,13 @@ create table Book ( NOTE: Support for entity associations is not required by this specification. +==== Entity Names + +Each entity must be assigned an _entity name_ by the provider. +By default, this must be the unqualified Java class name of the entity class. +A programming model for entity classes might provide a way to explicitly specify an entity name. +For example, Jakarta Persistence allows the entity name to be specified via the `name` member of the `@Entity` annotation. + ==== Entity Property Names Within an entity, property names must be unique ignoring case. For simple entity properties, the field or accessor method name serves as the entity property name. In the case of embedded classes, entity property names are computed by concatenating the field or accessor method names at each level, optionally joined by a delimiter. @@ -765,7 +772,7 @@ However, Jakarta Data implementations are strongly encouraged to support the fol Furthermore, implementations are encouraged to support `void` as the return type for a query which never returns a result. ==== -This specification defines the built-in `@Query` annotation, which may be used to specify a query in an arbitrary query language understood by the Jakarta Data provider. +This specification defines the built-in `@Query` annotation, which may be used to specify a query written in the <> defined in the next chapter. For example, using a named parameter: From c8ff958996baad30c108630d74236708e7352e84 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Tue, 5 Mar 2024 19:31:45 +0100 Subject: [PATCH 02/21] add note about requirements surrounding JDQL functions --- spec/src/main/asciidoc/query-language.asciidoc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/spec/src/main/asciidoc/query-language.asciidoc b/spec/src/main/asciidoc/query-language.asciidoc index 4c2032718..2bcf404cc 100644 --- a/spec/src/main/asciidoc/query-language.asciidoc +++ b/spec/src/main/asciidoc/query-language.asciidoc @@ -151,6 +151,10 @@ A _function call_ is the name of a JDQL function, followed by a parenthesized li When any argument expression of any function call evaluates to a null value, the whole function call evaluates to null. +NOTE: These functions cannot be emulated on every datastore. When a function cannot be reasonably emulated via the native query capabilities of the database, a JDQL implementation is not required to provide the function. + +NOTE: On the other hand, an implementation of JDQL might provide additional built-in functions, and might even allow invocation of user-defined functions. Section 4.7 of the Jakarta Persistence specification defines a set of functions that all JPQL implementations are required to provide, including `concat`, `substring`, `trim`, `locate`, `ceiling`, `floor`, `exp`, `ln`, `mod`, `power`, `round`, `sign`, `sqrt`, `cast`, `extract`, `coalesce`, and `nullif`. JDQL implementations are encouraged to support any of these functions which are reasonably implementable. + ==== Operator expressions The syntax of an _operator expression_ is given by the `scalar_expression` rule. Within an operator expression, parentheses indicate grouping. From b831e30a1fea70e58028e5864ddf66d9169c30d0 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Wed, 6 Mar 2024 09:45:11 +0100 Subject: [PATCH 03/21] Update spec/src/main/asciidoc/query-language.asciidoc Co-authored-by: Nathan Rauh --- spec/src/main/asciidoc/query-language.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/src/main/asciidoc/query-language.asciidoc b/spec/src/main/asciidoc/query-language.asciidoc index 2bcf404cc..ac8aed0c1 100644 --- a/spec/src/main/asciidoc/query-language.asciidoc +++ b/spec/src/main/asciidoc/query-language.asciidoc @@ -45,7 +45,7 @@ An _ordinal parameter_ is a decimal integer prefixed with the `?` character, for The character sequences `+`,`-`,`*`,`/`,`||`,`=`,`<`,`>`,`<>`,`<=`,`>=` are _operators_. -The characters `(`,`)`, and `,` are _punctuation characters_. +The characters `(`, `)`, and `,` are _punctuation characters_. ==== String literals From b95049c07785834fadcbdad1f8c795ae7d0a82b1 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Wed, 6 Mar 2024 10:31:47 +0100 Subject: [PATCH 04/21] clarify semantics of path expression involving null elements --- spec/src/main/asciidoc/query-language.asciidoc | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/spec/src/main/asciidoc/query-language.asciidoc b/spec/src/main/asciidoc/query-language.asciidoc index ac8aed0c1..37c67932d 100644 --- a/spec/src/main/asciidoc/query-language.asciidoc +++ b/spec/src/main/asciidoc/query-language.asciidoc @@ -14,6 +14,8 @@ The interpretation of an operator expression or literal expression of a given ty NOTE: This specification should not be interpreted to mandate an inefficient implementation of query language constructs in cases where the native behavior of the database varies from Java in such minor ways. That said, portability between Jakarta Data providers is maximized when their behavior is closest to the Java language. +Since a field of an entity may be null, a JDQL expression may evaluate to a null value. + === Lexical structure Lexical analysis requires recognition of the following token types: @@ -131,7 +133,12 @@ A _path expression_ is a period-separated list of Java identifiers, with syntax The type of the whole path expression is the type of the last element of the list. For example, `pages` is assigned the type `int`, `address` is assigned the type `org.example.Address`, and `address.street` is assigned the type `java.lang.String`. -When executed, a path expression is evaluated in the context of a given record of the queried entity type, and evaluates to the value of the entity field for the given record. +When a path expression is executed, each element of the path is evaluated in turn: + +- the first element of the path expression is evaluated in the context of a given record of the queried entity type, and evaluates to the value of the named entity field of the given record, and then +- each subsequent element is evaluated in the context of the result produced the previous element, and evaluates to the value of the named field of the result. + +If any element of a path expression evaluates to a null value, the whole path expression evaluates to a null value. ==== Function calls From d807f9cd7d612fdb0905adb662f9e94a2a0b852d Mon Sep 17 00:00:00 2001 From: Gavin King Date: Wed, 6 Mar 2024 10:37:06 +0100 Subject: [PATCH 05/21] Update spec/src/main/asciidoc/query-language.asciidoc Co-authored-by: Nathan Rauh --- spec/src/main/asciidoc/query-language.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/src/main/asciidoc/query-language.asciidoc b/spec/src/main/asciidoc/query-language.asciidoc index 37c67932d..2e4cd017a 100644 --- a/spec/src/main/asciidoc/query-language.asciidoc +++ b/spec/src/main/asciidoc/query-language.asciidoc @@ -45,7 +45,7 @@ An _ordinal parameter_ is a decimal integer prefixed with the `?` character, for ==== Operators and punctuation -The character sequences `+`,`-`,`*`,`/`,`||`,`=`,`<`,`>`,`<>`,`<=`,`>=` are _operators_. +The character sequences `+`, `-`, `*`, `/`, `||`, `=`, `<`, `>`, `<>`, `<=`, `>=` are _operators_. The characters `(`, `)`, and `,` are _punctuation characters_. From 451b0c34061c709716d1e46701086782e3fc92b3 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Wed, 6 Mar 2024 10:37:37 +0100 Subject: [PATCH 06/21] Update spec/src/main/asciidoc/query-language.asciidoc Co-authored-by: Nathan Rauh --- spec/src/main/asciidoc/query-language.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/src/main/asciidoc/query-language.asciidoc b/spec/src/main/asciidoc/query-language.asciidoc index 2e4cd017a..0e0ce5652 100644 --- a/spec/src/main/asciidoc/query-language.asciidoc +++ b/spec/src/main/asciidoc/query-language.asciidoc @@ -10,7 +10,7 @@ Every expression in a JDQL query is assigned a Java type. An implementation of J NOTE: An implementation of JDQL is permitted and encouraged to support additional types. Use of such types is not guaranteed to be portable between implementations. -The interpretation of an operator expression or literal expression of a given type is given by the interpretation of the equivalent expression in Java. However, the precise behavior of some queries might vary depending on the native semantics of queries on the underlying datastore. For example, numeric precision and overflow, string collation,and integer division are permitted to depart from the semantics of the Java language. +The interpretation of an operator expression or literal expression of a given type is given by the interpretation of the equivalent expression in Java. However, the precise behavior of some queries might vary depending on the native semantics of queries on the underlying datastore. For example, numeric precision and overflow, string collation, and integer division are permitted to depart from the semantics of the Java language. NOTE: This specification should not be interpreted to mandate an inefficient implementation of query language constructs in cases where the native behavior of the database varies from Java in such minor ways. That said, portability between Jakarta Data providers is maximized when their behavior is closest to the Java language. From 7f80fd4e4d92789ac0555898555e211cc6fc9a31 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Wed, 6 Mar 2024 13:13:03 +0100 Subject: [PATCH 07/21] further clarify requirements for path expressions --- spec/src/main/asciidoc/query-language.asciidoc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/spec/src/main/asciidoc/query-language.asciidoc b/spec/src/main/asciidoc/query-language.asciidoc index 0e0ce5652..3a3cf921b 100644 --- a/spec/src/main/asciidoc/query-language.asciidoc +++ b/spec/src/main/asciidoc/query-language.asciidoc @@ -133,10 +133,12 @@ A _path expression_ is a period-separated list of Java identifiers, with syntax The type of the whole path expression is the type of the last element of the list. For example, `pages` is assigned the type `int`, `address` is assigned the type `org.example.Address`, and `address.street` is assigned the type `java.lang.String`. +NOTE: Typically, the last element of a path expression is assigned a <>. Non-terminal path elements are usually assigned an embeddable type, if the element references an <>, or an entity type, if the element references an <>. However, since a Jakarta Data provider is not required to support embedded fields or associations, a JDQL implementation is not required to support compound path expressions. + When a path expression is executed, each element of the path is evaluated in turn: - the first element of the path expression is evaluated in the context of a given record of the queried entity type, and evaluates to the value of the named entity field of the given record, and then -- each subsequent element is evaluated in the context of the result produced the previous element, and evaluates to the value of the named field of the result. +- each subsequent element is evaluated in the context of the result produced the previous element (typically, and embeddable class or associated entity class), and evaluates to the value of the named field of the result. If any element of a path expression evaluates to a null value, the whole path expression evaluates to a null value. From 925b8e190b6d9111514111acb999f5bdf7fd0169 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Wed, 6 Mar 2024 13:25:55 +0100 Subject: [PATCH 08/21] clarify what happens if the database doesn't support: - sorting - counting, or - functions. --- spec/src/main/asciidoc/query-language.asciidoc | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/spec/src/main/asciidoc/query-language.asciidoc b/spec/src/main/asciidoc/query-language.asciidoc index 3a3cf921b..95eb7c31e 100644 --- a/spec/src/main/asciidoc/query-language.asciidoc +++ b/spec/src/main/asciidoc/query-language.asciidoc @@ -162,6 +162,8 @@ When any argument expression of any function call evaluates to a null value, the NOTE: These functions cannot be emulated on every datastore. When a function cannot be reasonably emulated via the native query capabilities of the database, a JDQL implementation is not required to provide the function. +If the JDQL implementation does not support a one of the standard functions explicitly listed above, it must throw `UnsupportedOperationException` when the function name occurs in a query. + NOTE: On the other hand, an implementation of JDQL might provide additional built-in functions, and might even allow invocation of user-defined functions. Section 4.7 of the Jakarta Persistence specification defines a set of functions that all JPQL implementations are required to provide, including `concat`, `substring`, `trim`, `locate`, `ceiling`, `floor`, `exp`, `ln`, `mod`, `power`, `round`, `sign`, `sqrt`, `cast`, `extract`, `coalesce`, and `nullif`. JDQL implementations are encouraged to support any of these functions which are reasonably implementable. ==== Operator expressions @@ -304,7 +306,13 @@ The `where` clause is always optional. When it is missing, there is no restricti ==== Select clause -The `select` clause, with syntax given by `select_clause`, specifies a list of expressions which are returned by the query. Each expression in the list is evaluated for each record which satisfies the restriction imposed by the `where` clause. Alternatively, the `select` clause may contain a single `count(*)` aggregate expression, which evaluates to the number of records which satisfy the restriction. +The `select` clause, with syntax given by `select_clause`, specifies a list of expressions which are returned by the query. Each expression in the list is evaluated for each record which satisfies the restriction imposed by the `where` clause. + +Alternatively, the `select` clause may contain a single `count(*)` aggregate expression, which evaluates to the number of records which satisfy the restriction. + +NOTE: If a datastore does not natively provide the ability to count query results, the Jakarta Data provider is strongly encouraged, but not required, to implement this operation by counting the query results in Java. + +If the JDQL implementation does not support `count(*)`, it must throw `UnsupportedOperationException` when this aggregate expression occurs in a query. The `select` clause is optional in `select` statements. When it is missing, the query returns the queried entity. @@ -318,6 +326,10 @@ The `order` clause (or `order by` clause), with syntax given by `orderby_clause` The `order` clause is always optional. When it is missing, and when no sort criteria are given as arguments to a parameter of the repository method, the order of the query results is undefined, and may not be deterministic. +NOTE: If a datastore does not natively provide the ability to sort query results, the Jakarta Data provider is strongly encouraged, but not required, to sort the query results in Java before returning the results to the client. + +If the Jakarta Data provider cannot satisfy a request for sorted query results, it must throw `UnsupportedOperationException`. + === Statements Finally, there are three kinds of _statement_: From 14a867ec3bf254850bf84d5c084cd466581018e4 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Wed, 6 Mar 2024 18:05:43 +0100 Subject: [PATCH 09/21] simplify select clause to a single path expression --- spec/src/antlr/JDQL.g4 | 2 +- spec/src/main/asciidoc/query-language.asciidoc | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/src/antlr/JDQL.g4 b/spec/src/antlr/JDQL.g4 index 7b98f15b9..34da0ba6b 100644 --- a/spec/src/antlr/JDQL.g4 +++ b/spec/src/antlr/JDQL.g4 @@ -15,7 +15,7 @@ update_item : state_field_path_expression '=' (scalar_expression | 'NULL'); select_clause : 'SELECT' select_list; select_list - : scalar_expression (',' scalar_expression)* // could limit to single select item and/or to state_field_path_expression + : state_field_path_expression | aggregate_expression ; aggregate_expression : 'COUNT' '(' '*' ')'; diff --git a/spec/src/main/asciidoc/query-language.asciidoc b/spec/src/main/asciidoc/query-language.asciidoc index 95eb7c31e..2ca0ea410 100644 --- a/spec/src/main/asciidoc/query-language.asciidoc +++ b/spec/src/main/asciidoc/query-language.asciidoc @@ -306,7 +306,7 @@ The `where` clause is always optional. When it is missing, there is no restricti ==== Select clause -The `select` clause, with syntax given by `select_clause`, specifies a list of expressions which are returned by the query. Each expression in the list is evaluated for each record which satisfies the restriction imposed by the `where` clause. +The `select` clause, with syntax given by `select_clause`, specifies a path expression which determines the values returned by the query. The path expression is evaluated for each record which satisfies the restriction imposed by the `where` clause, as specified in <>, and the value of the last element of the path expression is added to the query results. Alternatively, the `select` clause may contain a single `count(*)` aggregate expression, which evaluates to the number of records which satisfy the restriction. @@ -342,7 +342,7 @@ The clauses which can appear in a statement are given by the grammar for each ki ==== Select statements -A `select` statement, with syntax given by `select_statement`, returns data to the client. For each record which satisfies the restriction imposed by the `where` clause, a result is returned containing the values obtained by evaluating the scalar expressions in the `select` clause. Alternatively, for the case of `select count(*)`, the query returns the number of records which satisfied the restriction. +A `select` statement, with syntax given by `select_statement`, returns data to the client. For each record which satisfies the restriction imposed by the `where` clause, a result is returned containing the value obtained by evaluating the path expression in the `select` clause. Alternatively, for the case of `select count(*)`, the query returns the number of records which satisfied the restriction. ==== Update statements From 613a267b9c7ce8a417395053ca995d23dfc73dc5 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Wed, 6 Mar 2024 18:07:05 +0100 Subject: [PATCH 10/21] add LOCAL TIME --- spec/src/antlr/JDQL.g4 | 1 + spec/src/main/asciidoc/query-language.asciidoc | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/spec/src/antlr/JDQL.g4 b/spec/src/antlr/JDQL.g4 index 34da0ba6b..3162c091f 100644 --- a/spec/src/antlr/JDQL.g4 +++ b/spec/src/antlr/JDQL.g4 @@ -76,6 +76,7 @@ function_expression special_expression : 'LOCAL' 'DATE' | 'LOCAL' 'DATETIME' + | 'LOCAL' 'IME' | 'TRUE' | 'FALSE' ; diff --git a/spec/src/main/asciidoc/query-language.asciidoc b/spec/src/main/asciidoc/query-language.asciidoc index 2ca0ea410..2369ea801 100644 --- a/spec/src/main/asciidoc/query-language.asciidoc +++ b/spec/src/main/asciidoc/query-language.asciidoc @@ -102,7 +102,7 @@ When executed, a literal expression evaluates to its literal value. The special values `true` and `false` are assigned the type `boolean`, and evaluate to their literal values. -The special values `local date` and `local datetime` are assigned the types `java.time.LocalDate` and `java.time.LocalDateTime`, and evaluate to the current date and current datetime of the database server, respectively. +The special values `local date`, `local time`, and `local datetime` are assigned the types `java.time.LocalDate`, `java.time.LocalTime`, and `java.time.LocalDateTime`, and evaluate to the current date and current datetime of the database server, respectively. The syntax for special values is given by the `special_expression` grammar rule. From 659ef96746dbc49254f4c84b32bb5a334c183578 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Wed, 6 Mar 2024 18:15:22 +0100 Subject: [PATCH 11/21] define enum order --- spec/src/main/asciidoc/query-language.asciidoc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/spec/src/main/asciidoc/query-language.asciidoc b/spec/src/main/asciidoc/query-language.asciidoc index 2369ea801..bbaa43346 100644 --- a/spec/src/main/asciidoc/query-language.asciidoc +++ b/spec/src/main/asciidoc/query-language.asciidoc @@ -324,6 +324,8 @@ The `set` clause, with syntax given by `set_clause`, specifies a list of updates The `order` clause (or `order by` clause), with syntax given by `orderby_clause`, specifies a lexicographic order for the query results, that is, a list of entity fields used to sort the records which satisfy the restriction imposed by the `where` clause. The keywords `asc` and `desc` specify that a given field should be sorted in ascending or descending order respectively; when neither is specified, ascending order is the default. +By default, the order of values of a Java enumerated type is determined by the integer returned by `ordinal()`. A programming model for entity classes might allow control over the order of `enum` values. For example, Jakarta Persistence allows this via the `@Enumerated` annotation. + The `order` clause is always optional. When it is missing, and when no sort criteria are given as arguments to a parameter of the repository method, the order of the query results is undefined, and may not be deterministic. NOTE: If a datastore does not natively provide the ability to sort query results, the Jakarta Data provider is strongly encouraged, but not required, to sort the query results in Java before returning the results to the client. From 352b511944bf9fd13815801561fb8b8604dc913f Mon Sep 17 00:00:00 2001 From: Gavin King Date: Thu, 7 Mar 2024 16:23:28 +0100 Subject: [PATCH 12/21] undefine enum order --- spec/src/main/asciidoc/query-language.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/src/main/asciidoc/query-language.asciidoc b/spec/src/main/asciidoc/query-language.asciidoc index bbaa43346..28eadb691 100644 --- a/spec/src/main/asciidoc/query-language.asciidoc +++ b/spec/src/main/asciidoc/query-language.asciidoc @@ -324,7 +324,7 @@ The `set` clause, with syntax given by `set_clause`, specifies a list of updates The `order` clause (or `order by` clause), with syntax given by `orderby_clause`, specifies a lexicographic order for the query results, that is, a list of entity fields used to sort the records which satisfy the restriction imposed by the `where` clause. The keywords `asc` and `desc` specify that a given field should be sorted in ascending or descending order respectively; when neither is specified, ascending order is the default. -By default, the order of values of a Java enumerated type is determined by the integer returned by `ordinal()`. A programming model for entity classes might allow control over the order of `enum` values. For example, Jakarta Persistence allows this via the `@Enumerated` annotation. +NOTE: This specification does not define an order for the sorting of Java `enum` values, which is provider-dependent. A programming model for entity classes might allow control over the order of `enum` values. For example, Jakarta Persistence allows this via the `@Enumerated` annotation. The `order` clause is always optional. When it is missing, and when no sort criteria are given as arguments to a parameter of the repository method, the order of the query results is undefined, and may not be deterministic. From ebef49458ce1a0d2454a4c775862e094c55df1b7 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Sat, 9 Mar 2024 16:07:09 +0100 Subject: [PATCH 13/21] split out new section dealing with ordering/comparison --- .../src/main/asciidoc/query-language.asciidoc | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/spec/src/main/asciidoc/query-language.asciidoc b/spec/src/main/asciidoc/query-language.asciidoc index 28eadb691..f9c693335 100644 --- a/spec/src/main/asciidoc/query-language.asciidoc +++ b/spec/src/main/asciidoc/query-language.asciidoc @@ -259,11 +259,27 @@ The equality and inequality operators are `=`, `<>`, `<`, `>`, `< - For wrapper types, these operators are satisfied if both operands evaluate to non-null values, and the equivalent operator expression involving primitives would be satisfied. - For other types, these operators are evaluated according to the native semantics of the database. +The operands of an equality or inequality operator must have the same type. + NOTE: Portability is maximized when Jakarta Data providers interpret equality and inequality operators in a manner consistent with the implementation of `Object.equals()` or `Comparable.compareTo()` for the assigned Java type. -NOTE: For string values, a database might have a different collation algorithm to Java. In evaluating an inequality involving string operands, an implementation of JDQL is not required to emulate Java collation. +==== Ordering -The operands of an equality or inequality operator must have the same type. +Every <> can, in principle, be equipped with a total order. An order for a type determines the result of inequality comparisons, and the effect of the <>. + +For numeric types, and for date, time, and datetime types, the total order is unique and completely determined by the semantics of the type. JDQL implementations must sort these types according to their natural order, that is, the order in JDQL must agree with the order defined by Java. + +Boolean values must be ordered so that `false < true` is satisfied. + +For other types, there is at least some freedom in the choice of order. Usually, the order is determined by the native semantics of the database. Note that: + +- Textual data is represented in JDQL as the type `java.lang.String`. Strings are in general ordered lexicographically, but the ordering also depends on the character set and collation used by the database server. Applications must not assume that the order agrees with the `compareTo()` method of `java.lang.String`. In evaluating an inequality involving string operands, an implementation of JDQL is not required to emulate Java collation. + +- Binary data is represented in JDQL as the type `byte[]`. Binary data is in general ordered lexicographically with respect to the constituent bytes. However, since this ordering is rarely meaningful, this specification does not require implementations of JDQL to respect it. + +- This specification does not define an order for the sorting of Java `enum` values, which is provider-dependent. A programming model for entity classes might allow control over the order of `enum` values. For example, Jakarta Persistence allows this via the `@Enumerated` annotation. + +- This specification does not define an order for UUID values, which is provider-dependent. ==== Logical operators @@ -324,8 +340,6 @@ The `set` clause, with syntax given by `set_clause`, specifies a list of updates The `order` clause (or `order by` clause), with syntax given by `orderby_clause`, specifies a lexicographic order for the query results, that is, a list of entity fields used to sort the records which satisfy the restriction imposed by the `where` clause. The keywords `asc` and `desc` specify that a given field should be sorted in ascending or descending order respectively; when neither is specified, ascending order is the default. -NOTE: This specification does not define an order for the sorting of Java `enum` values, which is provider-dependent. A programming model for entity classes might allow control over the order of `enum` values. For example, Jakarta Persistence allows this via the `@Enumerated` annotation. - The `order` clause is always optional. When it is missing, and when no sort criteria are given as arguments to a parameter of the repository method, the order of the query results is undefined, and may not be deterministic. NOTE: If a datastore does not natively provide the ability to sort query results, the Jakarta Data provider is strongly encouraged, but not required, to sort the query results in Java before returning the results to the client. From c3a7adbf663e4f9f6c64a125c1a1d29803a9d0db Mon Sep 17 00:00:00 2001 From: Gavin King Date: Sat, 9 Mar 2024 16:41:50 +0100 Subject: [PATCH 14/21] example of from-clause inference --- spec/src/main/asciidoc/query-language.asciidoc | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/spec/src/main/asciidoc/query-language.asciidoc b/spec/src/main/asciidoc/query-language.asciidoc index f9c693335..7ab194efa 100644 --- a/spec/src/main/asciidoc/query-language.asciidoc +++ b/spec/src/main/asciidoc/query-language.asciidoc @@ -310,9 +310,25 @@ The interpretation and effect of each clause in this list is influenced by claus The `from` clause, with syntax given by `from_clause`, specifies an _entity name_ which identifies the queried entity. Path expressions occurring in later clauses are interpreted with respect to this entity. That is, the first element of each path expression in the query must be a persistent field of the entity named in the `from` clause. The entity name is a Java identifier, usually the unqualified name of the entity class, as specified in <>. +NOTE: The syntax of the `update` statement is irregular, with the `from` keyword implied. That is, the syntax _should_ be `update from Entity`, but for historical reasons it is simply `update Entity`. + The `from` clause is optional in `select` statements. When it is missing, the queried entity is determined by the return type of the repository method, or, if the return type is not an entity type, by the primary entity type of the repository. -NOTE: The syntax of the `update` statement is irregular, with the `from` keyword implied. That is, the syntax _should_ be `update from Entity`, but for historical reasons it is simply `update Entity`. +For example, this repository method declaration: + +[source,java] +---- +@Query("where title like :title") +List booksByType(String title); +---- + +is equivalent to: + +[source,java] +---- +@Query("from Book where title like :title") +List booksByType(String title); +---- ==== Where clause From a4157bf2c0b9b0add9705792da069be07c0154e3 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Tue, 12 Mar 2024 11:11:33 +0100 Subject: [PATCH 15/21] failure to sort should be a DataException @njr-11 wanted this, to allow for runtime failure --- spec/src/main/asciidoc/query-language.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/src/main/asciidoc/query-language.asciidoc b/spec/src/main/asciidoc/query-language.asciidoc index 7ab194efa..e81195661 100644 --- a/spec/src/main/asciidoc/query-language.asciidoc +++ b/spec/src/main/asciidoc/query-language.asciidoc @@ -360,7 +360,7 @@ The `order` clause is always optional. When it is missing, and when no sort crit NOTE: If a datastore does not natively provide the ability to sort query results, the Jakarta Data provider is strongly encouraged, but not required, to sort the query results in Java before returning the results to the client. -If the Jakarta Data provider cannot satisfy a request for sorted query results, it must throw `UnsupportedOperationException`. +If the Jakarta Data provider cannot satisfy a request for sorted query results, it must throw `DataException`. === Statements From edd3725f2337c9f87690e599f96bc954d2cf49bd Mon Sep 17 00:00:00 2001 From: Gavin King Date: Tue, 12 Mar 2024 11:25:47 +0100 Subject: [PATCH 16/21] clarify that unsupported functions can be rejected at compile time --- spec/src/main/asciidoc/query-language.asciidoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/src/main/asciidoc/query-language.asciidoc b/spec/src/main/asciidoc/query-language.asciidoc index e81195661..404cc4609 100644 --- a/spec/src/main/asciidoc/query-language.asciidoc +++ b/spec/src/main/asciidoc/query-language.asciidoc @@ -162,7 +162,7 @@ When any argument expression of any function call evaluates to a null value, the NOTE: These functions cannot be emulated on every datastore. When a function cannot be reasonably emulated via the native query capabilities of the database, a JDQL implementation is not required to provide the function. -If the JDQL implementation does not support a one of the standard functions explicitly listed above, it must throw `UnsupportedOperationException` when the function name occurs in a query. +If the JDQL implementation does not support a one of the standard functions explicitly listed above, it must throw `UnsupportedOperationException` when the function name occurs in a query. Alternatively, the Jakarta Data provider is permitted to reject a repository method declaration at compilation time if its `@Query` annotation uses an unsupported function. NOTE: On the other hand, an implementation of JDQL might provide additional built-in functions, and might even allow invocation of user-defined functions. Section 4.7 of the Jakarta Persistence specification defines a set of functions that all JPQL implementations are required to provide, including `concat`, `substring`, `trim`, `locate`, `ceiling`, `floor`, `exp`, `ln`, `mod`, `power`, `round`, `sign`, `sqrt`, `cast`, `extract`, `coalesce`, and `nullif`. JDQL implementations are encouraged to support any of these functions which are reasonably implementable. @@ -344,7 +344,7 @@ Alternatively, the `select` clause may contain a single `count(*)` aggregate exp NOTE: If a datastore does not natively provide the ability to count query results, the Jakarta Data provider is strongly encouraged, but not required, to implement this operation by counting the query results in Java. -If the JDQL implementation does not support `count(*)`, it must throw `UnsupportedOperationException` when this aggregate expression occurs in a query. +If the JDQL implementation does not support `count(*)`, it must throw `UnsupportedOperationException` when this aggregate expression occurs in a query. Alternatively, the Jakarta Data provider is permitted to reject a repository method declaration at compilation time if its `@Query` annotation uses the unsupported aggregate expression. The `select` clause is optional in `select` statements. When it is missing, the query returns the queried entity. From d13676c787fdd446256897c43bddf4fc5595fa15 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Tue, 12 Mar 2024 11:57:49 +0100 Subject: [PATCH 17/21] add discussion of operator precedence to text (it's already defined by the grammar) --- spec/src/main/asciidoc/query-language.asciidoc | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/spec/src/main/asciidoc/query-language.asciidoc b/spec/src/main/asciidoc/query-language.asciidoc index 404cc4609..a15f24ea4 100644 --- a/spec/src/main/asciidoc/query-language.asciidoc +++ b/spec/src/main/asciidoc/query-language.asciidoc @@ -170,6 +170,14 @@ NOTE: On the other hand, an implementation of JDQL might provide additional buil The syntax of an _operator expression_ is given by the `scalar_expression` rule. Within an operator expression, parentheses indicate grouping. +All binary infix operators are left-associative. The relative precedence, from highest to lowest precedence, is given by: + +1. `*` and `/`, +2. `+` and `-`, +3. `||`. + +The unary prefix operators `+` and `-` have higher precedence than the binary infix operators. Thus, `2 * -3 + 5` means `(2 * (-3)) + 5` and evaluates to `-1`. + The concatenation operator `||` is assigned the type `java.lang.String`. Its operand expressions must also be of type `java.lang.String`. When executed, a concatenation operator expression evaluates to a new string concatenating the strings to which its arguments evaluate. The numeric operators `+`, `-`, `*`, and `/` have the same meaning for primitive numeric types they have in Java, and operator expression involving these operators are assigned the types they would be assigned in Java. @@ -293,6 +301,8 @@ This specification leaves undefined the interpretation of the `not` operator whe CAUTION: A compliant implementation of JDQL might feature SQL/JPQL-style ternary logic, where `not n > 0` is an unsatisfied logical expression when `n` evaluates to null, or it might feature binary logic where the same expression is considered satisfied. Application programmers should take great care when using the `not` operator with scalar expressions involving `null` values. +Syntactically, logical operators are parsed with lower precedence than <> and other <>. The `not` operator has higher precedence than `and` and `or`. The `and` operator has higher precedence than `or`. + === Clauses Each JDQL statement is built from a sequence of _clauses_. The beginning of a clause is identified by a keyword: `from`, `where`, `select`, `set`, or `order`. From ebbda9d1931fbd593702cdcefbe688f3aaf46561 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Tue, 12 Mar 2024 23:12:58 +0100 Subject: [PATCH 18/21] Apply suggestions from code review Co-authored-by: Nathan Rauh --- spec/src/antlr/JDQL.g4 | 2 +- spec/src/main/asciidoc/query-language.asciidoc | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/spec/src/antlr/JDQL.g4 b/spec/src/antlr/JDQL.g4 index 3162c091f..f9458ce08 100644 --- a/spec/src/antlr/JDQL.g4 +++ b/spec/src/antlr/JDQL.g4 @@ -76,7 +76,7 @@ function_expression special_expression : 'LOCAL' 'DATE' | 'LOCAL' 'DATETIME' - | 'LOCAL' 'IME' + | 'LOCAL' 'TIME' | 'TRUE' | 'FALSE' ; diff --git a/spec/src/main/asciidoc/query-language.asciidoc b/spec/src/main/asciidoc/query-language.asciidoc index a15f24ea4..3e682b33e 100644 --- a/spec/src/main/asciidoc/query-language.asciidoc +++ b/spec/src/main/asciidoc/query-language.asciidoc @@ -6,7 +6,7 @@ NOTE: A Jakarta Data provider backed by access to a relational database might ch === Type system -Every expression in a JDQL query is assigned a Java type. An implementation of JDQL is required to support the Java types listed in <>, that is: primitive types, `String`, `LocalDate`, `LocalDateTime`, `LocalTime`, and `Instant`, `java.util.UUID`, `java.math.BigInteger` and `java.math.BigDecimal`, `byte[]`, and user-defined `enum` types. +Every expression in a JDQL query is assigned a Java type. An implementation of JDQL is required to support the Java types listed in <>, that is: primitive types, `String`, `LocalDate`, `LocalDateTime`, `LocalTime`, and `Instant`, `java.util.UUID`, `java.math.BigInteger` and `java.math.BigDecimal`, `byte[]`, and `enum` types. NOTE: An implementation of JDQL is permitted and encouraged to support additional types. Use of such types is not guaranteed to be portable between implementations. @@ -162,7 +162,7 @@ When any argument expression of any function call evaluates to a null value, the NOTE: These functions cannot be emulated on every datastore. When a function cannot be reasonably emulated via the native query capabilities of the database, a JDQL implementation is not required to provide the function. -If the JDQL implementation does not support a one of the standard functions explicitly listed above, it must throw `UnsupportedOperationException` when the function name occurs in a query. Alternatively, the Jakarta Data provider is permitted to reject a repository method declaration at compilation time if its `@Query` annotation uses an unsupported function. +If the JDQL implementation does not support a standard function explicitly listed above, it must throw `UnsupportedOperationException` when the function name occurs in a query. Alternatively, the Jakarta Data provider is permitted to reject a repository method declaration at compilation time if its `@Query` annotation uses an unsupported function. NOTE: On the other hand, an implementation of JDQL might provide additional built-in functions, and might even allow invocation of user-defined functions. Section 4.7 of the Jakarta Persistence specification defines a set of functions that all JPQL implementations are required to provide, including `concat`, `substring`, `trim`, `locate`, `ceiling`, `floor`, `exp`, `ln`, `mod`, `power`, `round`, `sign`, `sqrt`, `cast`, `extract`, `coalesce`, and `nullif`. JDQL implementations are encouraged to support any of these functions which are reasonably implementable. @@ -238,12 +238,12 @@ All operands must have the same type. A `between` expression, with syntax given by `between_expression` is satisfied when its operands all evaluate to non-null values, and, if the `not` keyword is missing, its left operand evaluates to a value which is: -- larger than or equal to the value take by its middle operand, and +- larger than or equal to the value taken by its middle operand, and - smaller than or equal to the value taken by its right operand. Or, if the `not` keyword occurs, the left operand must evaluate to a value which is: -- strictly smaller than to the value take by its middle operand, or +- strictly smaller than to the value taken by its middle operand, or - strictly larger than the value taken by its right operand. All three operands must have the same type. @@ -294,7 +294,7 @@ For other types, there is at least some freedom in the choice of order. Usually, The logical operators are `and`, `or`, and `not`. - An `and` operator expression is satisfied if and only if both its operands are satisfied. -- An `or` operator expression is satisfied if and only if at least one of its operands are satisfied. +- An `or` operator expression is satisfied if at least one of its operands is satisfied. - A `not` operator expression is never satisfied if its operand _is_ satisfied. This specification leaves undefined the interpretation of the `not` operator when its operand _is not_ satisfied. @@ -344,7 +344,7 @@ List booksByType(String title); The `where` clause, with syntax given by `where_clause`, specifies a conditional expression used to restrict the records returned, deleted, or updated by the query. Only records for which the conditional expression is satisfied are returned, deleted, or updated. -The `where` clause is always optional. When it is missing, there is no restriction, and, and all records are of the queried entity type are returned, deleted, or updated. +The `where` clause is always optional. When it is missing, there is no restriction, and all records of the queried entity type are returned, deleted, or updated. ==== Select clause @@ -366,7 +366,7 @@ The `set` clause, with syntax given by `set_clause`, specifies a list of updates The `order` clause (or `order by` clause), with syntax given by `orderby_clause`, specifies a lexicographic order for the query results, that is, a list of entity fields used to sort the records which satisfy the restriction imposed by the `where` clause. The keywords `asc` and `desc` specify that a given field should be sorted in ascending or descending order respectively; when neither is specified, ascending order is the default. -The `order` clause is always optional. When it is missing, and when no sort criteria are given as arguments to a parameter of the repository method, the order of the query results is undefined, and may not be deterministic. +The `order` clause is always optional. When it is missing, and when no sort criteria are given as arguments to a parameter of the repository method, the order of the query results is undefined, and might not be deterministic. NOTE: If a datastore does not natively provide the ability to sort query results, the Jakarta Data provider is strongly encouraged, but not required, to sort the query results in Java before returning the results to the client. From b761d12857f142cf830cada5222c7694d92ab365 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Fri, 15 Mar 2024 23:51:46 +0100 Subject: [PATCH 19/21] 'count(*)' -> 'count(this)' for compatibility with latest revision of JPA 3.2 --- spec/src/antlr/JDQL.g4 | 2 +- spec/src/main/asciidoc/query-language.asciidoc | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/spec/src/antlr/JDQL.g4 b/spec/src/antlr/JDQL.g4 index f9458ce08..37166daaf 100644 --- a/spec/src/antlr/JDQL.g4 +++ b/spec/src/antlr/JDQL.g4 @@ -18,7 +18,7 @@ select_list : state_field_path_expression | aggregate_expression ; -aggregate_expression : 'COUNT' '(' '*' ')'; +aggregate_expression : 'COUNT' '(' 'THIS' ')'; orderby_clause : 'ORDER' 'BY' orderby_item (',' orderby_item)*; orderby_item : state_field_path_expression ('ASC' | 'DESC'); diff --git a/spec/src/main/asciidoc/query-language.asciidoc b/spec/src/main/asciidoc/query-language.asciidoc index 3e682b33e..25bb34cc6 100644 --- a/spec/src/main/asciidoc/query-language.asciidoc +++ b/spec/src/main/asciidoc/query-language.asciidoc @@ -350,11 +350,11 @@ The `where` clause is always optional. When it is missing, there is no restricti The `select` clause, with syntax given by `select_clause`, specifies a path expression which determines the values returned by the query. The path expression is evaluated for each record which satisfies the restriction imposed by the `where` clause, as specified in <>, and the value of the last element of the path expression is added to the query results. -Alternatively, the `select` clause may contain a single `count(*)` aggregate expression, which evaluates to the number of records which satisfy the restriction. +Alternatively, the `select` clause may contain a single `count(this)` aggregate expression, which evaluates to the number of records which satisfy the restriction. NOTE: If a datastore does not natively provide the ability to count query results, the Jakarta Data provider is strongly encouraged, but not required, to implement this operation by counting the query results in Java. -If the JDQL implementation does not support `count(*)`, it must throw `UnsupportedOperationException` when this aggregate expression occurs in a query. Alternatively, the Jakarta Data provider is permitted to reject a repository method declaration at compilation time if its `@Query` annotation uses the unsupported aggregate expression. +If the JDQL implementation does not support `count(this)`, it must throw `UnsupportedOperationException` when this aggregate expression occurs in a query. Alternatively, the Jakarta Data provider is permitted to reject a repository method declaration at compilation time if its `@Query` annotation uses the unsupported aggregate expression. The `select` clause is optional in `select` statements. When it is missing, the query returns the queried entity. @@ -384,7 +384,7 @@ The clauses which can appear in a statement are given by the grammar for each ki ==== Select statements -A `select` statement, with syntax given by `select_statement`, returns data to the client. For each record which satisfies the restriction imposed by the `where` clause, a result is returned containing the value obtained by evaluating the path expression in the `select` clause. Alternatively, for the case of `select count(*)`, the query returns the number of records which satisfied the restriction. +A `select` statement, with syntax given by `select_statement`, returns data to the client. For each record which satisfies the restriction imposed by the `where` clause, a result is returned containing the value obtained by evaluating the path expression in the `select` clause. Alternatively, for the case of `select count(this)`, the query returns the number of records which satisfied the restriction. ==== Update statements From 43c88751ec5adf885c1d7343ea42703b2d475aff Mon Sep 17 00:00:00 2001 From: Gavin King Date: Sat, 16 Mar 2024 00:08:22 +0100 Subject: [PATCH 20/21] remove Contains and Empty operators from QbMN --- README.adoc | 8 +++---- api/src/main/java/module-info.java | 17 -------------- spec/src/main/asciidoc/repository.asciidoc | 26 +++++++--------------- 3 files changed, 12 insertions(+), 39 deletions(-) diff --git a/README.adoc b/README.adoc index 2f5922a65..299d3dbbf 100644 --- a/README.adoc +++ b/README.adoc @@ -102,10 +102,10 @@ By embracing type-safe access to entity attributes with Jakarta Data's static me [source,java] ---- -List found = products.findByNameContains(searchPattern, Order.by( - _Product.price.desc(), - _Product.name.asc(), - _Product.id.asc())); +List found = products.findByNameLike(searchPattern, Order.by( + _Product.price.desc(), + _Product.name.asc(), + _Product.id.asc())); ---- === Maven diff --git a/api/src/main/java/module-info.java b/api/src/main/java/module-info.java index d1a2c6a5d..38c66bdd0 100644 --- a/api/src/main/java/module-info.java +++ b/api/src/main/java/module-info.java @@ -289,23 +289,6 @@ * {@code findByAgeBetween(minAge, maxAge)} * Key-value
Wide-Column * - * {@code Contains} - * collections, strings - * For Collection attributes, requires that the entity's attribute value, - * which is a collection, includes the parameter value. - * For String attributes, requires that any substring of the entity's attribute value - * match the entity's attribute value, which can be a pattern with wildcard characters. - * {@code findByRecipientsContains(email)} - *
{@code findByDescriptionNotContains("refurbished")} - * Key-value
Wide-Column
Document - * - * {@code Empty} - * collections - * Requires that the entity's attribute is an empty collection or has a null value. - * {@code countByPhoneNumbersEmpty()} - *
{@code findByInviteesNotEmpty()} - * Key-value
Wide-Column
Document
Graph - * * {@code EndsWith} * strings * Requires that the characters at the end of the entity's attribute value diff --git a/spec/src/main/asciidoc/repository.asciidoc b/spec/src/main/asciidoc/repository.asciidoc index 49789621e..7ab5bb802 100644 --- a/spec/src/main/asciidoc/repository.asciidoc +++ b/spec/src/main/asciidoc/repository.asciidoc @@ -649,10 +649,10 @@ Example usage: [source,java] ---- -List found = products.findByNameContains(searchPattern, - _Product.price.desc(), - _Product.name.asc(), - _Product.id.asc()); +List found = products.findByNameLike(searchPattern, + _Product.price.desc(), + _Product.name.asc(), + _Product.id.asc()); ---- @@ -935,7 +935,7 @@ Query methods allow developers to create database queries using method naming co ::= "First" [] ::= { ("And" | "Or") } ::= ["IgnoreCase"] ["Not"] [] - ::= "Contains" | "EndsWith" | "StartsWith" | "LessThan"| "LessThanEqual" | "GreaterThan" | "GreaterThanEqual" | "Between" | "Empty" | "Like" | "In" | "Null" | "True" | "False" + ::= "EndsWith" | "StartsWith" | "LessThan"| "LessThanEqual" | "GreaterThan" | "GreaterThanEqual" | "Between" | "Like" | "In" | "Null" | "True" | "False" ::= | "_" ::= ::= { } @@ -1012,16 +1012,6 @@ Jakarta Data implementations must support the following list of Query by Method |findByDateBetween |Key-value, Wide-Column -|Contains -|For Collection attributes, matches if the collection includes the value. For String attributes, a substring of the String must match the value, which can be a pattern. -|findByPhoneNumbersContains -|Key-value, Wide-Column, Document - -|Empty -|Find results where the property is an empty collection or has a null value. -|deleteByPendingTasksEmpty -|Key-value, Wide-Column, Document, Graph - |EndsWith |Matches String values with the given ending, which can be a pattern. |findByProductNameEndsWith @@ -1487,7 +1477,7 @@ In this example, the application uses a cursor to request pages in forward and p ---- @Repository public interface Products extends CrudRepository { - CursoredPage findByNameContains(String namePattern, PageRequest pageRequest); + CursoredPage findByNameLike(String namePattern, PageRequest pageRequest); } ---- @@ -1499,7 +1489,7 @@ float priceMidpoint = 50.0f; PageRequest pageRequest = Order.by(_Product.price.asc(), _Product.id.asc()) .pageSize(10) .afterKey(priceMidpoint, 0L); -CursoredPage moreExpensive = products.findByNameContains(pattern, pageRequest); +CursoredPage moreExpensive = products.findByNameLike(pattern, pageRequest); ---- Obtaining the previous 10 products: @@ -1510,7 +1500,7 @@ if (moreExpensive.hasContent()) pageRequest = pageRequest.beforeCursor(moreExpensive.getCursor(0)); else pageRequest = pageRequest.beforeKey(priceMidpoint, 1L); -CursoredPage lessExpensive = products.findByNameContains(pattern, pageRequest); +CursoredPage lessExpensive = products.findByNameLike(pattern, pageRequest); ---- ===== Example with Combined Sort Criteria From 7805071e9512e05ff2f4aa889a416b44b5445875 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Sat, 16 Mar 2024 16:37:51 +0100 Subject: [PATCH 21/21] for @njr-11 - bring Contains back to life (but only from Strings) - add Empty to reserved words --- api/src/main/java/module-info.java | 38 +++++++++++++--------- spec/src/main/asciidoc/repository.asciidoc | 7 +++- 2 files changed, 29 insertions(+), 16 deletions(-) diff --git a/api/src/main/java/module-info.java b/api/src/main/java/module-info.java index 38c66bdd0..71b6b9c53 100644 --- a/api/src/main/java/module-info.java +++ b/api/src/main/java/module-info.java @@ -289,32 +289,39 @@ * {@code findByAgeBetween(minAge, maxAge)} * Key-value
Wide-Column * - * {@code EndsWith} + * {@code Contains} + * strings + * Requires that a substring of the entity's attribute value + * matches the parameter value, which can be a pattern. + * {@code findByNameContains(middleName)} + * Key-value
Wide-Column
Document
Graph + * + * {@code EndsWith} * strings * Requires that the characters at the end of the entity's attribute value * match the parameter value, which can be a pattern. * {@code findByNameEndsWith(surname)} * Key-value
Wide-Column
Document
Graph * - * {@code False} + * {@code False} * boolean * Requires that the entity's attribute value has a boolean value of false. * {@code findByCanceledFalse()} * Key-value
Wide-Column * - * {@code GreaterThan} + * {@code GreaterThan} * numeric, strings, time * Requires that the entity's attribute value be larger than the parameter value. * {@code findByStartTimeGreaterThan(startedAfter)} * Key-value
Wide-Column * - * {@code GreaterThanEqual} + * {@code GreaterThanEqual} * numeric, strings, time * Requires that the entity's attribute value be at least as big as the parameter value. * {@code findByAgeGreaterThanEqual(minimumAge)} * Key-value
Wide-Column * - * {@code IgnoreCase} + * {@code IgnoreCase} * strings * Requires case insensitive comparison. For query conditions * as well as ordering, the {@code IgnoreCase} keyword can be @@ -323,58 +330,58 @@ *
{@code findByZipcodeOrderByStreetIgnoreCaseAscHouseNumAsc(55904)} * Key-value
Wide-Column
Document
Graph * - * {@code In} + * {@code In} * all attribute types * Requires that the entity's attribute value be within the list that is the parameter value. * {@code findByNameIn(names)} * Key-value
Wide-Column
Document
Graph * - * {@code LessThan} + * {@code LessThan} * numeric, strings, time * Requires that the entity's attribute value be less than the parameter value. * {@code findByStartTimeLessThan(startedBefore)} * Key-value
Wide-Column * - * {@code LessThanEqual} + * {@code LessThanEqual} * numeric, strings, time * Requires that the entity's attribute value be at least as small as the parameter value. * {@code findByAgeLessThanEqual(maximumAge)} * Key-value
Wide-Column * - * {@code Like} + * {@code Like} * strings * Requires that the entity's attribute value match the parameter value, which can be a pattern. * {@code findByNameLike(namePattern)} * Key-value
Wide-Column
Document
Graph * - * {@code Not} + * {@code Not} * condition * Negates a condition. * {@code deleteByNameNotLike(namePattern)} *
{@code findByStatusNot("RUNNING")} * Key-value
Wide-Column * - * {@code Null} + * {@code Null} * nullable types * Requires that the entity's attribute has a null value. * {@code findByEndTimeNull()} *
{@code findByAgeNotNull()} * Key-value
Wide-Column
Document
Graph * - * {@code Or} + * {@code Or} * conditions * Requires at least one of the two conditions to be satisfied in order to match an entity. * {@code findByPriceLessThanEqualOrDiscountGreaterThanEqual(maxPrice, minDiscount)} * Key-value
Wide-Column * - * {@code StartsWith} + * {@code StartsWith} * strings * Requires that the characters at the beginning of the entity's attribute value * match the parameter value, which can be a pattern. * {@code findByNameStartsWith(firstTwoLetters)} * Key-value
Wide-Column
Document
Graph * - * {@code True} + * {@code True} * boolean * Requires that the entity's attribute value has a boolean value of true. * {@code findByAvailableTrue()} @@ -442,7 +449,8 @@ * future releases without introducing breaking changes to applications. *

*

- * Reserved for query conditions: {@code AbsoluteValue}, {@code CharCount}, {@code ElementCount}, + * Reserved for query conditions: {@code AbsoluteValue}, {@code CharCount}, + * {@code ElementCount}, {@code Empty} * {@code Rounded}, {@code RoundedDown}, {@code RoundedUp}, {@code Trimmed}, * {@code WithDay}, {@code WithHour}, {@code WithMinute}, {@code WithMonth}, * {@code WithQuarter}, {@code WithSecond}, {@code WithWeek}, {@code WithYear}. diff --git a/spec/src/main/asciidoc/repository.asciidoc b/spec/src/main/asciidoc/repository.asciidoc index 7ab5bb802..f7f825f5b 100644 --- a/spec/src/main/asciidoc/repository.asciidoc +++ b/spec/src/main/asciidoc/repository.asciidoc @@ -935,7 +935,7 @@ Query methods allow developers to create database queries using method naming co ::= "First" [] ::= { ("And" | "Or") } ::= ["IgnoreCase"] ["Not"] [] - ::= "EndsWith" | "StartsWith" | "LessThan"| "LessThanEqual" | "GreaterThan" | "GreaterThanEqual" | "Between" | "Like" | "In" | "Null" | "True" | "False" + ::= "Contains" | "EndsWith" | "StartsWith" | "LessThan"| "LessThanEqual" | "GreaterThan" | "GreaterThanEqual" | "Between" | "Like" | "In" | "Null" | "True" | "False" ::= | "_" ::= ::= { } @@ -1012,6 +1012,11 @@ Jakarta Data implementations must support the following list of Query by Method |findByDateBetween |Key-value, Wide-Column +|Contains +|Matches String values with the given substring, which can be a pattern. +|findByProductNameContains +|Key-value, Wide-Column, Document, Graph + |EndsWith |Matches String values with the given ending, which can be a pattern. |findByProductNameEndsWith