From d93f32d35f1b4c769a32fb33c0dedb0532463008 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Kraus?= Date: Fri, 15 Sep 2023 15:03:19 +0200 Subject: [PATCH] jakartaee/persistence#395 - JPQL cast() function jakartaee/persistence#438 - add Expression.equalTo() and Expression.notEqualTo() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomáš Kraus --- .../jpa/querydef/CriteriaBuilderImpl.java | 39 ++++---- .../internal/jpa/querydef/ExpressionImpl.java | 90 +++++++++++++------ .../jpa/querydef/InternalSelection.java | 5 -- .../jpa/querydef/ParameterExpressionImpl.java | 5 +- .../internal/jpa/querydef/SelectionImpl.java | 11 ++- 5 files changed, 96 insertions(+), 54 deletions(-) diff --git a/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/internal/jpa/querydef/CriteriaBuilderImpl.java b/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/internal/jpa/querydef/CriteriaBuilderImpl.java index 9276e1c922e..4b16d34ea73 100644 --- a/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/internal/jpa/querydef/CriteriaBuilderImpl.java +++ b/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/internal/jpa/querydef/CriteriaBuilderImpl.java @@ -27,6 +27,7 @@ import java.time.temporal.Temporal; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; @@ -72,7 +73,7 @@ import org.eclipse.persistence.jpa.JpaCriteriaBuilder; import org.eclipse.persistence.queries.ReportQuery; -import static org.eclipse.persistence.internal.jpa.querydef.InternalSelection.currentNode; +import static org.eclipse.persistence.internal.jpa.querydef.ExpressionImpl.currentNode; public class CriteriaBuilderImpl implements JpaCriteriaBuilder, Serializable { @@ -260,7 +261,7 @@ public > Expression greatest(Expression x) if (((InternalSelection)x).getCurrentNode() == null){ throw new IllegalArgumentException(ExceptionLocalization.buildMessage("OPERATOR_EXPRESSION_IS_CONJUNCTION")); } - return new ExpressionImpl(this.metamodel, x.getJavaType(),((InternalSelection)x).getCurrentNode().maximum()); + return new ExpressionImpl<>(this.metamodel, x.getJavaType(), ((InternalSelection)x).getCurrentNode().maximum()); } /** @@ -276,7 +277,7 @@ public > Expression least(Expression x){ if (((InternalSelection)x).getCurrentNode() == null){ throw new IllegalArgumentException(ExceptionLocalization.buildMessage("OPERATOR_EXPRESSION_IS_CONJUNCTION")); } - return new ExpressionImpl(this.metamodel, x.getJavaType(),((InternalSelection)x).getCurrentNode().minimum()); + return new ExpressionImpl<>(this.metamodel, x.getJavaType(),((InternalSelection)x).getCurrentNode().minimum()); } /** @@ -883,11 +884,11 @@ public > Predicate between(Expression> buildList(Expression... expressions){ - List> list = new ArrayList<>(); - for(Expression exp : expressions){ - list.add(exp); - } + protected List> buildList(Expression... expressions) { + // Immutable List causes test failures. + // Those lists are usually small (size 1-2) and modifications are rare. Default list size is too much. + List> list = new ArrayList<>(expressions.length + 2); + Collections.addAll(list, expressions); return list; } @@ -1485,7 +1486,7 @@ public Expression literal(T value){ if (value == null) { throw new IllegalArgumentException( ExceptionLocalization.buildMessage("jpa_criteriaapi_null_literal_value", new Object[]{})); } - return new ExpressionImpl(metamodel, (Class) (value.getClass()), new ConstantExpression(value, new ExpressionBuilder()), value); + return ExpressionImpl.createLiteral(value, metamodel); } /** @@ -1495,8 +1496,8 @@ public Expression literal(T value){ * @return null expression literal */ @Override - public Expression nullLiteral(Class resultClass){ - return new ExpressionImpl(metamodel, resultClass, new ConstantExpression(null, new ExpressionBuilder()), null); + public Expression nullLiteral(Class resultClass) { + return ExpressionImpl.createLiteral(null, metamodel, resultClass); } /** @@ -1505,14 +1506,14 @@ public Expression nullLiteral(Class resultClass){ * @return expression literal */ protected Expression internalLiteral(T value){ - return new ExpressionImpl(metamodel, (Class) (value == null? null: value.getClass()), new ConstantExpression(value, new ExpressionBuilder()), value); + return ExpressionImpl.createLiteral(value, metamodel); } // parameters: /** * Create a parameter. - * * Create a parameter expression. + * * @param paramClass parameter class * @return parameter expression */ @@ -2297,7 +2298,7 @@ public Expression locate(Expression x, String pattern, int from */ @Override public Expression currentDate(){ - return new ExpressionImpl(metamodel, ClassConstants.SQLDATE, new ExpressionBuilder().currentDateDate()); + return new ExpressionImpl<>(metamodel, ClassConstants.SQLDATE, new ExpressionBuilder().currentDateDate()); } /** @@ -2307,7 +2308,7 @@ public Expression currentDate(){ */ @Override public Expression currentTimestamp(){ - return new ExpressionImpl(metamodel, ClassConstants.TIMESTAMP, new ExpressionBuilder().currentTimeStamp()); + return new ExpressionImpl<>(metamodel, ClassConstants.TIMESTAMP, new ExpressionBuilder().currentTimeStamp()); } /** @@ -2317,7 +2318,7 @@ public Expression currentTimestamp(){ */ @Override public Expression currentTime(){ - return new ExpressionImpl(metamodel, ClassConstants.TIME, new ExpressionBuilder().currentTime()); + return new ExpressionImpl<>(metamodel, ClassConstants.TIME, new ExpressionBuilder().currentTime()); } /** @@ -2327,7 +2328,7 @@ public Expression currentTime(){ */ @Override public Expression localDateTime() { - return new ExpressionImpl(metamodel, ClassConstants.LOCAL_DATETIME, new ExpressionBuilder().localDateTime()); + return new ExpressionImpl<>(metamodel, ClassConstants.LOCAL_DATETIME, new ExpressionBuilder().localDateTime()); } /** @@ -2337,7 +2338,7 @@ public Expression localDateTime() { */ @Override public Expression localDate() { - return new ExpressionImpl(metamodel, ClassConstants.LOCAL_DATE, new ExpressionBuilder().localDate()); + return new ExpressionImpl<>(metamodel, ClassConstants.LOCAL_DATE, new ExpressionBuilder().localDate()); } /** @@ -2347,7 +2348,7 @@ public Expression localDate() { */ @Override public Expression localTime() { - return new ExpressionImpl(metamodel, ClassConstants.LOCAL_TIME, new ExpressionBuilder().localTime()); + return new ExpressionImpl<>(metamodel, ClassConstants.LOCAL_TIME, new ExpressionBuilder().localTime()); } @Override diff --git a/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/internal/jpa/querydef/ExpressionImpl.java b/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/internal/jpa/querydef/ExpressionImpl.java index 76dddfa41f8..1c96d460aa9 100644 --- a/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/internal/jpa/querydef/ExpressionImpl.java +++ b/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/internal/jpa/querydef/ExpressionImpl.java @@ -24,6 +24,8 @@ import jakarta.persistence.metamodel.Metamodel; import org.eclipse.persistence.descriptors.ClassDescriptor; +import org.eclipse.persistence.expressions.ExpressionBuilder; +import org.eclipse.persistence.internal.expressions.ConstantExpression; import org.eclipse.persistence.internal.jpa.metamodel.MetamodelImpl; import org.eclipse.persistence.internal.localization.ExceptionLocalization; import org.eclipse.persistence.sessions.Project; @@ -45,66 +47,82 @@ public class ExpressionImpl extends SelectionImpl implements Expression protected boolean isLiteral; protected Object literal; - protected ExpressionImpl(Metamodel metamodel, Class javaType, org.eclipse.persistence.expressions.Expression expressionNode){ + // Non literal value + protected ExpressionImpl(Metamodel metamodel, Class javaType, org.eclipse.persistence.expressions.Expression expressionNode){ super(javaType, expressionNode); this.metamodel = metamodel; } - public ExpressionImpl(Metamodel metamodel, Class javaType, org.eclipse.persistence.expressions.Expression expressionNode, Object value){ - super(javaType, expressionNode); + // Literal value + public ExpressionImpl(Metamodel metamodel, Class javaType, org.eclipse.persistence.expressions.Expression expressionNode, Object value) { + this(metamodel, javaType, expressionNode, value, true, null); + } + + // Allows complete clone of the instance + private ExpressionImpl( + Metamodel metamodel, + Class javaType, + org.eclipse.persistence.expressions.Expression expressionNode, + Object value, + boolean isLiteral, + String alias) { + super(javaType, expressionNode, alias); this.metamodel = metamodel; this.literal = value; - this.isLiteral = true; + this.isLiteral = isLiteral; } @Override public Expression as(Class type) { - Project project = ((MetamodelImpl)metamodel).getProject(); - if (project != null){ - ClassDescriptor descriptor = project.getClassDescriptor(javaType); - if (descriptor != null && descriptor.hasInheritance()){ - descriptor = descriptor.getInheritancePolicy().getSubclassDescriptor(type); - if (descriptor != null){ - return buildExpressionForAs(type); - } - } - } - return (Expression) this; + // JPA spec: This shall return new instance according to spec, but historical code does only cast + return buildExpressionForAs(type); } - // TODO-API-3.2 @Override public Expression cast(Class type) { - throw new UnsupportedOperationException("Jakarta Persistence 3.2 API was not implemented yet"); + // JPA spec: New instance with provided Java type + return new ExpressionImpl<>(metamodel, type, currentNode, literal, isLiteral, alias); } + @SuppressWarnings("unchecked") protected Expression buildExpressionForAs(Class type) { return (Expression) this; } - - // TODO-API-3.2 @Override public Predicate equalTo(Expression value) { - throw new UnsupportedOperationException("Jakarta Persistence 3.2 API was not implemented yet"); + return new CompoundExpressionImpl( + this.metamodel, + this.currentNode.equal(currentNode(value)), + List.of(this, value), + "equals"); } - // TODO-API-3.2 @Override public Predicate equalTo(Object value) { - throw new UnsupportedOperationException("Jakarta Persistence 3.2 API was not implemented yet"); + return new CompoundExpressionImpl( + this.metamodel, + this.currentNode.equal(value), + List.of(this, createLiteral(value, metamodel)), + "equals"); } - // TODO-API-3.2 @Override public Predicate notEqualTo(Expression value) { - throw new UnsupportedOperationException("Jakarta Persistence 3.2 API was not implemented yet"); + return new CompoundExpressionImpl( + this.metamodel, + this.currentNode.equal(currentNode(value)), + List.of(this, value), + "not equal"); } - // TODO-API-3.2 @Override public Predicate notEqualTo(Object value) { - throw new UnsupportedOperationException("Jakarta Persistence 3.2 API was not implemented yet"); + return new CompoundExpressionImpl( + this.metamodel, + this.currentNode.equal(value), + List.of(this, createLiteral(value, metamodel)), + "not equal"); } @Override @@ -229,4 +247,24 @@ public void findRootAndParameters(CommonAbstractCriteriaImpl criteriaQuery){ //no-op because an expression will have no root } + // Literal Expression factory method + static Expression createLiteral(T value, Metamodel metamodel, Class resultClass) { + return new ExpressionImpl( + metamodel, + resultClass, + new ConstantExpression(value, new ExpressionBuilder()), value); + + } + + // Literal Expression factory method + @SuppressWarnings("unchecked") + static Expression createLiteral(T value, Metamodel metamodel) { + return createLiteral(value, metamodel, value == null ? null : (Class) value.getClass()); + } + + // Shortcut to return current expression node + static org.eclipse.persistence.expressions.Expression currentNode(Expression expression) { + return ((InternalSelection)expression).getCurrentNode(); + } + } diff --git a/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/internal/jpa/querydef/InternalSelection.java b/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/internal/jpa/querydef/InternalSelection.java index b5bdcfe8217..27b0f648c7e 100644 --- a/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/internal/jpa/querydef/InternalSelection.java +++ b/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/internal/jpa/querydef/InternalSelection.java @@ -40,9 +40,4 @@ public interface InternalSelection { boolean isRoot(); boolean isConstructor(); - // Shortcut to return current expression node - static org.eclipse.persistence.expressions.Expression currentNode(Expression expression) { - return ((InternalSelection)expression).getCurrentNode(); - } - } diff --git a/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/internal/jpa/querydef/ParameterExpressionImpl.java b/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/internal/jpa/querydef/ParameterExpressionImpl.java index aadb30cb637..088338c65cf 100644 --- a/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/internal/jpa/querydef/ParameterExpressionImpl.java +++ b/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/internal/jpa/querydef/ParameterExpressionImpl.java @@ -90,9 +90,10 @@ public Integer getPosition(){ * native query when the implementation does not support this * use. */ - @Override + @Override + @SuppressWarnings("unchecked") public Class getParameterType(){ - return this.javaType; + return (Class) this.javaType; } @Override diff --git a/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/internal/jpa/querydef/SelectionImpl.java b/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/internal/jpa/querydef/SelectionImpl.java index ef08dec53ff..610c1757490 100644 --- a/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/internal/jpa/querydef/SelectionImpl.java +++ b/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/internal/jpa/querydef/SelectionImpl.java @@ -38,7 +38,7 @@ */ public abstract class SelectionImpl implements Selection, InternalSelection, Serializable{ - protected Class javaType; + protected Class javaType; protected Expression currentNode; /** @@ -52,11 +52,18 @@ public Expression getCurrentNode() { protected String alias; - public SelectionImpl(Class javaType, Expression expressionNode){ + public SelectionImpl(Class javaType, Expression expressionNode){ this.javaType = javaType; this.currentNode = expressionNode; } + // Allows complete clone of the instance + protected SelectionImpl(Class javaType, Expression expressionNode, String alias) { + this.javaType = javaType; + this.currentNode = expressionNode; + this.alias = alias; + } + //SELECTION /** * Assign an alias to the selection.