Skip to content

Commit 3d1e6b4

Browse files
authored
Jakarta Persistence 3.2 new feature - JPQL functions ID(), VERSION() (#2108)
Implementation plus unit test according jakartaee/persistence#596 There are two new JPQL functions: - `ID(...)` to fetch Entity `@Id` value. There is support for single or composite primary key - `VERSION(...)` to fetch attribute value from attribute marked by `@Version` annotation These functions are specific as they are exist in JPQL only, but not in SQL like other JPQL functions where are usually similar SQL function/procedure at the DB side. Signed-off-by: Radek Felcman <[email protected]>
1 parent 9c4b266 commit 3d1e6b4

File tree

41 files changed

+1489
-62
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+1489
-62
lines changed

Diff for: foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/jpa/jpql/ExpressionBuilderVisitor.java

+2-8
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@
178178
* @author John Bracken
179179
*/
180180
@SuppressWarnings("nls")
181-
final class ExpressionBuilderVisitor implements EclipseLinkExpressionVisitor {
181+
final class ExpressionBuilderVisitor extends JPQLFunctionsAbstractBuilder implements EclipseLinkExpressionVisitor {
182182

183183
/**
184184
* This visitor creates a list by retrieving either the single child or the children of the
@@ -197,11 +197,6 @@ final class ExpressionBuilderVisitor implements EclipseLinkExpressionVisitor {
197197
*/
198198
private Comparator<Class<?>> numericTypeComparator;
199199

200-
/**
201-
* The context used to query information about the application metadata.
202-
*/
203-
private final JPQLQueryContext queryContext;
204-
205200
/**
206201
* The EclipseLink {@link Expression} that represents a visited parsed
207202
* {@link org.eclipse persistence.jpa.query.parser.Expression Expression}
@@ -226,9 +221,8 @@ final class ExpressionBuilderVisitor implements EclipseLinkExpressionVisitor {
226221
* cached information
227222
*/
228223
ExpressionBuilderVisitor(JPQLQueryContext queryContext) {
229-
super();
224+
super(queryContext);
230225
this.type = new Class<?>[1];
231-
this.queryContext = queryContext;
232226
}
233227

234228
private void appendJoinVariables(org.eclipse.persistence.jpa.jpql.parser.Expression expression,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/*
2+
* Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved.
3+
*
4+
* This program and the accompanying materials are made available under the
5+
* terms of the Eclipse Public License v. 2.0 which is available at
6+
* http://www.eclipse.org/legal/epl-2.0,
7+
* or the Eclipse Distribution License v. 1.0 which is available at
8+
* http://www.eclipse.org/org/documents/edl-v10.php.
9+
*
10+
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
11+
*/
12+
13+
// Contributors:
14+
// Oracle - initial API and implementation
15+
//
16+
package org.eclipse.persistence.internal.jpa.jpql;
17+
18+
import org.eclipse.persistence.descriptors.ClassDescriptor;
19+
import org.eclipse.persistence.descriptors.VersionLockingPolicy;
20+
import org.eclipse.persistence.internal.helper.DatabaseField;
21+
import org.eclipse.persistence.jpa.jpql.parser.EclipseLinkAnonymousExpressionVisitor;
22+
import org.eclipse.persistence.jpa.jpql.parser.IdExpression;
23+
import org.eclipse.persistence.jpa.jpql.parser.IdentificationVariable;
24+
import org.eclipse.persistence.jpa.jpql.parser.StateFieldPathExpression;
25+
import org.eclipse.persistence.jpa.jpql.parser.VersionExpression;
26+
import org.eclipse.persistence.mappings.DatabaseMapping;
27+
28+
import java.util.List;
29+
30+
/**
31+
* JPQL exclusive ID(), VERSION() functions/expressions are transformed there to StateFieldPathExpression.
32+
* It should be used in the future for another JPQL functions/expressions which are not available at the DB level.
33+
* E.g. For Entity e with idAttr as a primary key: <code>SELECT ID(e) FROM Entity e -> SELECT e.idAttr FROM Entity e</code>
34+
* For Entity e with versionAttr as a version attribute: <code>SELECT VERSION(e) FROM Entity e -> SELECT e.versionAttr FROM Entity e</code>
35+
*
36+
* @author Radek Felcman
37+
* @since 5.0
38+
*/
39+
public abstract class JPQLFunctionsAbstractBuilder extends EclipseLinkAnonymousExpressionVisitor {
40+
41+
/**
42+
* The {@link JPQLQueryContext} is used to query information about the application metadata and
43+
* cached information.
44+
*/
45+
final JPQLQueryContext queryContext;
46+
47+
protected JPQLFunctionsAbstractBuilder(JPQLQueryContext queryContext) {
48+
this.queryContext = queryContext;
49+
}
50+
51+
/**
52+
* For Entity e with idAttr as a primary key: <code>SELECT ID(e) FROM Entity e -> SELECT e.idAttr FROM Entity e</code>
53+
*
54+
* @param expression The {@link IdExpression} to visit
55+
*/
56+
@Override
57+
public void visit(IdExpression expression) {
58+
//Fetch identification variable info
59+
IdentificationVariable identificationVariable = (IdentificationVariable) expression.getExpression();
60+
String variableText = identificationVariable.getText();
61+
String variableName = identificationVariable.getVariableName();
62+
63+
//Get id attribute name
64+
ClassDescriptor descriptor = this.queryContext.getDeclaration(variableName).getDescriptor();
65+
List<DatabaseField> primaryKeyFields = descriptor.getPrimaryKeyFields();
66+
String idAttributeName = getIdAttributeNameByField(descriptor.getMappings(), primaryKeyFields.get(0));
67+
StateFieldPathExpression stateFieldPathExpression = new StateFieldPathExpression(expression.getParent(), variableText + "." + idAttributeName);
68+
expression.setStateFieldPathExpression(stateFieldPathExpression);
69+
70+
//Continue with created StateFieldPathExpression
71+
//It handle by ObjectBuilder booth @Id/primary key types (simple/composite)
72+
expression.getStateFieldPathExpression().accept(this);
73+
}
74+
75+
/**
76+
* For Entity e with versionAttr as a version attribute: <code>SELECT VERSION(e) FROM Entity e -> SELECT e.versionAttr FROM Entity e</code>
77+
*
78+
* @param expression The {@link VersionExpression} to visit
79+
*/
80+
@Override
81+
public void visit(VersionExpression expression) {
82+
//Fetch identification variable info
83+
IdentificationVariable identificationVariable = (IdentificationVariable) expression.getExpression();
84+
String variableText = identificationVariable.getText();
85+
String variableName = identificationVariable.getVariableName();
86+
87+
//Get version attribute name
88+
ClassDescriptor descriptor = this.queryContext.getDeclaration(variableName).getDescriptor();
89+
String versionAttributeName = ((VersionLockingPolicy) descriptor.getOptimisticLockingPolicy()).getVersionMapping().getAttributeName();
90+
StateFieldPathExpression stateFieldPathExpression = new StateFieldPathExpression(expression.getParent(), variableText + "." + versionAttributeName);
91+
expression.setStateFieldPathExpression(stateFieldPathExpression);
92+
93+
//Continue with created StateFieldPathExpression
94+
expression.getStateFieldPathExpression().accept(this);
95+
}
96+
97+
private String getIdAttributeNameByField(List<DatabaseMapping> databaseMappings, DatabaseField field) {
98+
for (DatabaseMapping mapping : databaseMappings) {
99+
if (field.equals(mapping.getField()) || mapping.isPrimaryKeyMapping()) {
100+
return mapping.getAttributeName();
101+
}
102+
}
103+
return null;
104+
}
105+
}

Diff for: foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/jpa/jpql/ReadAllQueryBuilder.java

+3-11
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2006, 2021 Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2006, 2024 Oracle and/or its affiliates. All rights reserved.
33
*
44
* This program and the accompanying materials are made available under the
55
* terms of the Eclipse Public License v. 2.0 which is available at
@@ -16,7 +16,6 @@
1616
package org.eclipse.persistence.internal.jpa.jpql;
1717

1818
import org.eclipse.persistence.jpa.jpql.parser.CollectionExpression;
19-
import org.eclipse.persistence.jpa.jpql.parser.EclipseLinkAnonymousExpressionVisitor;
2019
import org.eclipse.persistence.jpa.jpql.parser.Expression;
2120
import org.eclipse.persistence.jpa.jpql.parser.IdentificationVariable;
2221
import org.eclipse.persistence.jpa.jpql.parser.NullExpression;
@@ -36,19 +35,13 @@
3635
* @author Pascal Filion
3736
* @author John Bracken
3837
*/
39-
final class ReadAllQueryBuilder extends EclipseLinkAnonymousExpressionVisitor {
38+
final class ReadAllQueryBuilder extends JPQLFunctionsAbstractBuilder {
4039

4140
/**
4241
* The query that was created based on the type of select clause.
4342
*/
4443
ReadAllQuery query;
4544

46-
/**
47-
* The {@link JPQLQueryContext} is used to query information about the application metadata and
48-
* cached information.
49-
*/
50-
private final JPQLQueryContext queryContext;
51-
5245
/**
5346
* The {@link Expression} being visited.
5447
*/
@@ -61,8 +54,7 @@ final class ReadAllQueryBuilder extends EclipseLinkAnonymousExpressionVisitor {
6154
* cached information
6255
*/
6356
ReadAllQueryBuilder(JPQLQueryContext queryContext) {
64-
super();
65-
this.queryContext = queryContext;
57+
super(queryContext);
6658
}
6759

6860
private void initializeReadAllQuery() {

Diff for: foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/jpa/jpql/ReportItemBuilder.java

+3-10
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2006, 2023 Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2006, 2024 Oracle and/or its affiliates. All rights reserved.
33
* Copyright (c) 2019 IBM Corporation. All rights reserved.
44
*
55
* This program and the accompanying materials are made available under the
@@ -94,7 +94,7 @@
9494
* @author John Bracken
9595
*/
9696
@SuppressWarnings("nls")
97-
final class ReportItemBuilder extends EclipseLinkAnonymousExpressionVisitor {
97+
final class ReportItemBuilder extends JPQLFunctionsAbstractBuilder {
9898

9999
/**
100100
* The visitor responsible to visit the constructor items.
@@ -111,12 +111,6 @@ final class ReportItemBuilder extends EclipseLinkAnonymousExpressionVisitor {
111111
*/
112112
private ReportQuery query;
113113

114-
/**
115-
* The {@link JPQLQueryContext} is used to query information about the application metadata and
116-
* cached information.
117-
*/
118-
private final JPQLQueryContext queryContext;
119-
120114
/**
121115
* If the select expression is aliased with a result variable, then temporarily cache it so it
122116
* can be used as the attribute name.
@@ -138,10 +132,9 @@ final class ReportItemBuilder extends EclipseLinkAnonymousExpressionVisitor {
138132
* tree representation of the JPQL query
139133
*/
140134
ReportItemBuilder(JPQLQueryContext queryContext, ReportQuery query) {
141-
super();
135+
super(queryContext);
142136
this.query = query;
143137
this.type = new Class<?>[1];
144-
this.queryContext = queryContext;
145138
}
146139

147140
private void addAttribute(String generateName, Expression queryExpression) {

Diff for: foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/jpa/jpql/TypeResolver.java

+2-8
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@
162162
* @author Pascal Filion
163163
*/
164164
@SuppressWarnings("nls")
165-
final class TypeResolver implements EclipseLinkExpressionVisitor {
165+
final class TypeResolver extends JPQLFunctionsAbstractBuilder implements EclipseLinkExpressionVisitor {
166166

167167
/**
168168
* This visitor is responsible to retrieve the {@link CollectionExpression} if it is visited.
@@ -179,11 +179,6 @@ final class TypeResolver implements EclipseLinkExpressionVisitor {
179179
*/
180180
private PathResolver pathResolver;
181181

182-
/**
183-
* The context used to query information about the application metadata and cached information.
184-
*/
185-
private final JPQLQueryContext queryContext;
186-
187182
/**
188183
* The well defined type, which does not have to be calculated.
189184
*/
@@ -201,8 +196,7 @@ final class TypeResolver implements EclipseLinkExpressionVisitor {
201196
* cached information
202197
*/
203198
TypeResolver(JPQLQueryContext queryContext) {
204-
super();
205-
this.queryContext = queryContext;
199+
super(queryContext);
206200
}
207201

208202
/**

Diff for: jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/version/TestVersioning.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,11 @@ public class TestVersioning {
4343
value = "UseNationalCharacterVaryingTypeForString=true")})
4444
private EntityManagerFactory emf;
4545

46-
private final static String qStr1 = "UPDATE TemporalVersionedEntity " +
46+
private final static String qStr1 = "UPDATE TemporalVersionedEntity " +
4747
"SET updatetimestamp = ?3 " +
4848
"WHERE id = ?1 AND updatetimestamp = ?2";
4949

50-
private final static String qStr2 = "UPDATE TemporalVersionedEntity2 " +
50+
private final static String qStr2 = "UPDATE TemporalVersionedEntity2 " +
5151
"SET version = ?3 " +
5252
"WHERE id = ?1 AND version = ?2";
5353

Diff for: jpa/eclipselink.jpa.testapps/jpa.test.jpql/src/main/resources/META-INF/persistence.xml

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<!--
22
3-
Copyright (c) 2018, 2022 Oracle and/or its affiliates. All rights reserved.
3+
Copyright (c) 2018, 2024 Oracle and/or its affiliates. All rights reserved.
44
55
This program and the accompanying materials are made available under the
66
terms of the Eclipse Public License v. 2.0 which is available at
@@ -40,6 +40,7 @@
4040
<class>org.eclipse.persistence.testing.models.jpa.advanced.Room</class>
4141
<class>org.eclipse.persistence.testing.models.jpa.advanced.SmallProject</class>
4242
<class>org.eclipse.persistence.testing.models.jpa.advanced.SimpleRoom</class>
43+
<class>org.eclipse.persistence.testing.models.jpa.advanced.Vegetable</class>
4344
<class>org.eclipse.persistence.testing.models.jpa.advanced.Woman</class>
4445
<class>org.eclipse.persistence.testing.models.jpa.advanced.WorldRank</class>
4546
<class>org.eclipse.persistence.testing.models.jpa.advanced.entities.EntyA</class>

0 commit comments

Comments
 (0)