diff --git a/api/src/main/java/jakarta/persistence/criteria/From.java b/api/src/main/java/jakarta/persistence/criteria/From.java index 33bab5c6..5fcd61c0 100644 --- a/api/src/main/java/jakarta/persistence/criteria/From.java +++ b/api/src/main/java/jakarta/persistence/criteria/From.java @@ -17,6 +17,7 @@ package jakarta.persistence.criteria; +import jakarta.persistence.metamodel.EntityType; import jakarta.persistence.metamodel.SingularAttribute; import jakarta.persistence.metamodel.CollectionAttribute; import jakarta.persistence.metamodel.ListAttribute; @@ -68,6 +69,40 @@ public interface From extends Path, FetchParent { */ From getCorrelationParent(); + /** + * Create and add an inner join to the given entity. + * @param entityClass the target entity class + * @return the resulting join + * @since 3.2 + */ + Join join(Class entityClass); + + /** + * Create and add a join to the given entity. + * @param entityClass the target entity class + * @param joinType join type + * @return the resulting join + * @since 3.2 + */ + Join join(Class entityClass, JoinType joinType); + + /** + * Create and add an inner join to the given entity. + * @param entity metamodel entity representing the join target + * @return the resulting join + * @since 3.2 + */ + Join join(EntityType entity); + + /** + * Create and add a join to the given entity. + * @param entity metamodel entity representing the join target + * @param joinType join type + * @return the resulting join + * @since 3.2 + */ + Join join(EntityType entity, JoinType joinType); + /** * Create an inner join to the specified single-valued * attribute. @@ -150,7 +185,7 @@ public interface From extends Path, FetchParent { */ MapJoin join(MapAttribute map, JoinType jt); - + //String-based: /** diff --git a/api/src/main/java/jakarta/persistence/criteria/Join.java b/api/src/main/java/jakarta/persistence/criteria/Join.java index ace76278..b7d2f0e4 100644 --- a/api/src/main/java/jakarta/persistence/criteria/Join.java +++ b/api/src/main/java/jakarta/persistence/criteria/Join.java @@ -58,8 +58,10 @@ public interface Join extends From { Predicate getOn(); /** - * Return the metamodel attribute corresponding to the join. - * @return metamodel attribute corresponding to the join + * Return the metamodel attribute representing the join + * target, if any, or null if the target of the join is an + * entity type. + * @return metamodel attribute or null */ Attribute getAttribute(); diff --git a/api/src/main/java/jakarta/persistence/criteria/JoinType.java b/api/src/main/java/jakarta/persistence/criteria/JoinType.java index 32d8c58f..b387fb08 100644 --- a/api/src/main/java/jakarta/persistence/criteria/JoinType.java +++ b/api/src/main/java/jakarta/persistence/criteria/JoinType.java @@ -17,22 +17,29 @@ package jakarta.persistence.criteria; /** - * Defines the three types of joins. + * Defines the four varieties of join. * - * Right outer joins and right outer fetch joins are not required - * to be supported. Applications that use RIGHT join - * types will not be portable. + *

Support for {@link #RIGHT} or {@link #FULL} joins is only + * required when these join types are natively supported by the + * database. Applications that depend on these join types are not + * portable between all SQL databases. * * @since 2.0 */ public enum JoinType { - /** Inner join. */ + /** + * Inner join. + */ INNER, - /** Left outer join. */ + /** + * Left outer join. + */ LEFT, - /** Right outer join. */ - RIGHT + /** + * Right outer join. + */ + RIGHT, } diff --git a/spec/src/main/asciidoc/ch04-query-language.adoc b/spec/src/main/asciidoc/ch04-query-language.adoc index 4b6c547a..84fd21e6 100644 --- a/spec/src/main/asciidoc/ch04-query-language.adoc +++ b/spec/src/main/asciidoc/ch04-query-language.adoc @@ -303,9 +303,18 @@ identification_variable_declaration ::= range_variable_declaration {join | fetch range_variable_declaration ::= entity_name [AS] identification_variable -join ::= join_spec join_association_path_expression [AS] identification_variable [join_condition] +join ::= range_join | path_join -fetch_join ::= join_spec FETCH join_association_path_expression +range_join ::= join_spec range_variable_declaration [join_condition] + +path_join ::= + join_spec join_association_path_expression [AS] identification_variable [join_condition] + +join_spec ::= [INNER | LEFT [OUTER]] JOIN + +fetch_join ::= fetch_join_spec FETCH join_association_path_expression + +fetch_join_spec ::= [INNER | LEFT [OUTER]] JOIN join_association_path_expression ::= join_collection_valued_path_expression | @@ -317,8 +326,6 @@ join_collection_valued_path_expression ::= identification_variable.{single_value join_single_valued_path_expression ::= identification_variable.{single_valued_embeddable_object_field.}*single_valued_object_field -join_spec ::= [LEFT [OUTER] | INNER] JOIN - join_condition ::= ON conditional_expression collection_member_declaration ::= IN (collection_valued_path_expression) [AS] identification_variable @@ -433,7 +440,7 @@ query (or subquery) in which it is defined and is also visible to any subqueries within that query scope that do not define an identification variable of the same name. -==== Range Variable Declarations +==== Range Variable Declarations [[a4766]] The syntax for declaring an identification variable as a range variable is similar to that of SQL; @@ -686,22 +693,43 @@ SELECT DISTINCT l.product FROM Order AS o JOIN o.lineItems l ---- -It is illegal to use a -_collection_valued_path_expression_ other than in the FROM clause of a -query except in an _empty_collection_comparison_expression,_ in a -_collection_member_expression_, or as an argument to the SIZE operator. +A _collection_valued_path_expression_ may only occur in: + +- the FROM clause of a query, +- an _empty_collection_comparison_expression,_ +- a _collection_member_expression_, or +- as an argument to the SIZE operator. + See <>, <>, and <>. ==== Joins -An inner join may be implicitly specified by -the use of a cartesian product in the FROM clause and a join condition -in the WHERE clause. In the absence of a join condition, this reduces to -the cartesian product. +JPQL defines the following varieties of join: -The main use case for this generalized style -of join is when a join condition does not involve a foreign key -relationship that is mapped to an entity relationship. +- inner joins, +- left outer joins, +- right outer joins, +- full outer joins. + +The semantics of each variety of join is identical to SQL, and the +syntax is borrowed from ANSI SQL. Providers are not required to +support right joins or full joins on databases which do not natively +support these join types. + +Every join has a target, either: + +- an entity-valued path expression, or +- an entity type (that is, range variable declaration, as already + specified in <>). + +An inner join may be implicitly specified by the use of a cartesian +product in the FROM clause and a join condition in the WHERE clause. +In the absence of a join condition, this reduces to the cartesian +product. + +The main use case for this generalized style of join is when a join +condition does not involve a foreign key relationship mapped to an +association between entities. Example: @@ -710,17 +738,24 @@ Example: SELECT c FROM Customer c, Employee e WHERE c.hatsize = e.shoesize ---- -In general, use of this style of inner join -(also referred to as theta-join) is less typical than explicitly defined -joins over relationships. +This style of inner join (sometimes called a "theta" join) is less +typical than explicitly defined joins over relationships. -The syntax for explicit join operations is as -follows: +The syntax for explicit join operations is given by: ---- -join ::= join_spec join_association_path_expression [AS] identification_variable [join_condition] +join ::= range_join | path_join + +range_join ::= join_spec range_variable_declaration [join_condition] + +path_join ::= + join_spec join_association_path_expression [AS] identification_variable [join_condition] -fetch_join ::= join_spec FETCH join_association_path_expression +join_spec ::= [INNER | LEFT [OUTER]] JOIN + +fetch_join ::= fetch_join_spec FETCH join_association_path_expression + +fetch_join_spec ::= [INNER | LEFT [OUTER]] JOIN join_association_path_expression ::= join_collection_valued_path_expression | @@ -734,51 +769,93 @@ join_collection_valued_path_expression ::= join_single_valued_path_expression ::= identification_variable.{single_valued_embeddable_object_field.}*single_valued_object_field -join_spec ::= [LEFT [OUTER] | INNER] JOIN - join_condition ::= ON conditional_expression ---- -The inner and outer join operation types -described in <>, <>, and <> are supported. +The inner and outer join operation types described in <>, <>, +and <> are supported. + +===== Inner Joins [[a4884]] + +The syntax for an inner join to an entity type is given by: + +---- +[INNER] JOIN range_variable_declaration [join_condition] +---- + +The keyword INNER is optional and does not affect the semantics +of the query. + +[source,sql] +---- +SELECT c +FROM Customer c + JOIN Order o ON o.customer.id = c.id +WHERE c.status = 1 +---- + +Or, equivalently: + +[source,sql] +---- +SELECT c +FROM Customer c + INNER JOIN Order o ON o.customer.id = c.id +WHERE c.status = 1 +---- + +These queries are equivalent to the following query involving +an implicit "theta" join: -===== Inner Joins (Relationship Joins) [[a4884]] +[source,sql] +---- +SELECT c +FROM Customer c, Order o +WHERE o.customer.id = c.id AND c.status = 1 +---- -The syntax for the inner join operation is +The syntax for an inner join over an association is given by: ---- [INNER] JOIN join_association_path_expression [AS] identification_variable [join_condition] ---- -For example, the query below joins over the -relationship between customers and orders. This type of join typically -equates to a join over a foreign key relationship in the database. +For example, the query below joins over the relationship between +customers and orders. This type of join typically equates to a +join over a foreign key relationship in the database. [source,sql] ---- -SELECT c FROM Customer c JOIN c.orders o WHERE c.status = 1 +SELECT c +FROM Customer c + JOIN c.orders o +WHERE c.status = 1 ---- -The keyword INNER may optionally be used: +Equivalently: [source,sql] ---- -SELECT c FROM Customer c INNER JOIN c.orders o WHERE c.status = 1 +SELECT c +FROM Customer c + INNER JOIN c.orders o +WHERE c.status = 1 ---- -This is equivalent to the following query -using the earlier IN construct, defined in -<>. It selects those customers of +This is equivalent to the following query using the earlier IN +construct, defined in <>. It selects those customers of status 1 for which at least one order exists: [source,sql] ---- -SELECT OBJECT(c) FROM Customer c, IN(c.orders) o WHERE c.status = 1 +SELECT OBJECT(c) +FROM Customer c, IN(c.orders) o +WHERE c.status = 1 ---- -The query below joins over _Employee_, -_ContactInfo_ and _Phone_. _ContactInfo_ is an embeddable class that -consists of an address and set of phones. _Phone_ is an entity. +The query below joins over _Employee_, _ContactInfo_ and _Phone_. +_ContactInfo_ is an embeddable class that consists of an address +and set of phones. _Phone_ is an entity. [source,sql] ---- @@ -787,27 +864,53 @@ FROM Employee e JOIN e.contactInfo c JOIN c.phones p WHERE c.address.zipcode = '95054' ---- -A join condition may be specified for an -inner join. This is equivalent to specification of the same condition in -the WHERE clause. +A join condition may be specified for an inner join. This is equivalent +to specification of the same condition in the WHERE clause. + +===== Outer Joins [[a4898]] + +The syntax for an outer join to an entity type is given by: + +---- +LEFT [OUTER] JOIN range_variable_declaration [join_condition] +---- + +The keyword OUTER is optional and does not affect the semantics of +the query. -===== Left Outer Joins [[a4898]] +[source,sql] +---- +SELECT c +FROM Customer c + LEFT JOIN Order o ON o.customer.id = c.id +WHERE c.status = 1 +---- -LEFT JOIN and LEFT OUTER JOIN are synonymous. -They enable the retrieval of a set of entities where matching values in -the join condition may be absent. +Or, equivalently: -The syntax for a left outer join is +[source,sql] +---- +SELECT c +FROM Customer c + LEFT OUTER JOIN Order o ON o.customer.id = c.id +WHERE c.status = 1 +---- + +Outer joins enable the retrieval of a set of entities where matching +values in the join condition may be absent. For example, the queries +above return _Customer_ instances with no matching _Order_. + +The syntax for an outer join over an association is given by: ---- LEFT [OUTER] JOIN join_association_path_expression [AS] identification_variable [join_condition] ---- -An outer join without a specified join -condition has an implicit join condition over the foreign key -relationship corresponding to the join_association_path_expression. It -would typically be mapped to a SQL outer join with an ON condition on -the foreign key relationship as in the queries below: +An association outer join without no explicit _join_condition_ has an +implicit join condition inferred from the foreign key relationship +mapped by the _join_association_path_expression_. Typically, a JPQL +join of this form is translated to a SQL outer join with an ON condition +specifying the foreign key relationship, as in the following examples. Jakarta Persistence query language: @@ -828,9 +931,9 @@ FROM Suppliers s LEFT JOIN Products p GROUP By s.name ---- -An outer join with an explicit ON condition -would cause an additional specified join condition to be added to the -generated SQL: +An explicit _join_condition_ (that is, an ON condition in the JOIN) +results in an additional restriction in the ON condition of the +generated SQL. Jakarta Persistence query language: @@ -852,8 +955,8 @@ FROM Suppliers s LEFT JOIN Products p GROUP BY s.name ---- -Note that the result of this query will be -different from that of the following query: +Note that the result of this query will be different from that of the +following query: [source,sql] ---- @@ -863,27 +966,27 @@ WHERE p.status = 'inStock' GROUP BY s.name ---- -The result of the latter query will exclude -suppliers who have no products in stock whereas the former query will -include them. +The result of the latter query will exclude suppliers who have no +products in stock whereas the former query will include them. -An important use case for LEFT JOIN is in -enabling the prefetching of related data items as a side effect of a -query. This is accomplished by specifying the LEFT JOIN as a FETCH JOIN -as described below. +An important use case for LEFT JOIN is in enabling the prefetching of +related data items as a side effect of a query. This is accomplished by +specifying the LEFT JOIN as a FETCH JOIN, as described below. ===== Fetch Joins [[a4931]] -A FETCH JOIN enables the fetching of an -association or element collection as a side effect of the execution of a -query. +A FETCH JOIN clause in a query results in eager fetching of an association +or element collection as a side effect of execution of the query. -The syntax for a fetch join is +The syntax for a fetch join is given by: ---- fetch_join ::= [LEFT [OUTER] | INNER] JOIN FETCH join_association_path_expression ---- +A FETCH JOIN must be an INNER or LEFT (OUTER) join. A FETCH JOIN does not +have an explicit join condition or identification variable. + The association referenced by the right side of the FETCH JOIN clause must be an association or element collection that is referenced from an entity or embeddable that is returned as a @@ -920,7 +1023,7 @@ the FROM clause of a subquery. ==== Collection Member Declarations An identification variable declared by a -collection_member_declaration ranges over values of a collection +_collection_member_declaration_ ranges over values of a collection obtained by navigation using a path expression. An identification variable of a collection @@ -2990,10 +3093,13 @@ from_clause ::= {, {identification_variable_declaration | collection_member_declaration}}* identification_variable_declaration ::= range_variable_declaration {join | fetch_join}* range_variable_declaration ::= entity_name [AS] identification_variable -join ::= join_spec join_association_path_expression [AS] identification_variable - [join_condition] -fetch_join ::= join_spec FETCH join_association_path_expression -join_spec ::= [LEFT [OUTER] | INNER] JOIN +join ::= range_join | path_join +range_join ::= join_spec range_variable_declaration [join_condition] +path_join ::= + join_spec join_association_path_expression [AS] identification_variable [join_condition] +join_spec ::= [INNER | LEFT [OUTER]] JOIN +fetch_join ::= fetch_join_spec FETCH join_association_path_expression +fetch_join_spec ::= [INNER | LEFT [OUTER]] JOIN join_condition ::= ON conditional_expression join_association_path_expression ::= join_collection_valued_path_expression | diff --git a/spec/src/main/asciidoc/ch06-criteria-api.adoc b/spec/src/main/asciidoc/ch06-criteria-api.adoc index a701d37c..6a0ed153 100644 --- a/spec/src/main/asciidoc/ch06-criteria-api.adoc +++ b/spec/src/main/asciidoc/ch06-criteria-api.adoc @@ -4150,11 +4150,8 @@ q.select(customer.get(Customer_.name)) .where(cb.equal(item.get(Item_.product).get(Product_.productType), "printer")); ---- -By default, the _join_ method defines an -inner join. Outer joins are defined by specifying a _JoinType_ argument. -Only left outer joins and left outer fetch joins are required to be -supported. Applications that make use of right outer joins or right -outer fetch joins will not be portable. +By default, the _join_ method defines an inner join. Outer joins are defined +by explicitly specifying a _JoinType_ argument. The following query uses a left outer join: