From 4820390280cb270dcdbf251cfcb83df405fc28a3 Mon Sep 17 00:00:00 2001 From: Christian Beikov Date: Sat, 9 Nov 2024 18:51:05 +0100 Subject: [PATCH] Workaround a MySQL limitation and cleanup JpaProvider --- .../blazebit/persistence/spi/JpaProvider.java | 9 -- ...stractUpdateCollectionCriteriaBuilder.java | 2 +- .../persistence/impl/CachingJpaProvider.java | 5 - .../impl/ResolvingQueryGenerator.java | 116 +++++++----------- .../eclipselink/EclipseLinkJpaProvider.java | 5 - .../hibernate/base/HibernateJpaProvider.java | 5 - 6 files changed, 46 insertions(+), 96 deletions(-) diff --git a/core/api/src/main/java/com/blazebit/persistence/spi/JpaProvider.java b/core/api/src/main/java/com/blazebit/persistence/spi/JpaProvider.java index fc56ad9b24..3eb50dd462 100644 --- a/core/api/src/main/java/com/blazebit/persistence/spi/JpaProvider.java +++ b/core/api/src/main/java/com/blazebit/persistence/spi/JpaProvider.java @@ -511,15 +511,6 @@ public interface JpaProvider { */ public boolean supportsTransientEntityAsParameter(); - /** - * Indicates if the provider needs associations in the ON clause to use their id. - * If needed, an expression like alias.association in the ON clause is rewritten to - * alias.association.id. - * - * @return true if required, else false - */ - public boolean needsAssociationToIdRewriteInOnClause(); - /** * Indicates if the provider needs associations in the ON clause to use their id. * If needed, an expression like alias.association in the ON clause is rewritten to diff --git a/core/impl/src/main/java/com/blazebit/persistence/impl/AbstractUpdateCollectionCriteriaBuilder.java b/core/impl/src/main/java/com/blazebit/persistence/impl/AbstractUpdateCollectionCriteriaBuilder.java index 41543c1e41..c4e80c8a5a 100644 --- a/core/impl/src/main/java/com/blazebit/persistence/impl/AbstractUpdateCollectionCriteriaBuilder.java +++ b/core/impl/src/main/java/com/blazebit/persistence/impl/AbstractUpdateCollectionCriteriaBuilder.java @@ -305,7 +305,7 @@ protected void prepareAndCheck(JoinVisitor parentVisitor) { if (!needsCheck) { return; } - boolean enableElementCollectionIdCutoff = collectionAttribute.getJoinTable() != null && !mainQuery.jpaProvider.needsAssociationToIdRewriteInOnClause(); + boolean enableElementCollectionIdCutoff = collectionAttribute.getJoinTable() != null; JpaUtils.expandBindings(setAttributeBindingMap, collectionColumnBindingMap, collectionAttributeEntries, ClauseType.SET, this, keyFunctionExpression, enableElementCollectionIdCutoff); super.prepareAndCheck(parentVisitor); } diff --git a/core/impl/src/main/java/com/blazebit/persistence/impl/CachingJpaProvider.java b/core/impl/src/main/java/com/blazebit/persistence/impl/CachingJpaProvider.java index 40dcd1b8df..b5055ab0cf 100644 --- a/core/impl/src/main/java/com/blazebit/persistence/impl/CachingJpaProvider.java +++ b/core/impl/src/main/java/com/blazebit/persistence/impl/CachingJpaProvider.java @@ -333,11 +333,6 @@ public boolean supportsTransientEntityAsParameter() { return jpaProvider.supportsTransientEntityAsParameter(); } - @Override - public boolean needsAssociationToIdRewriteInOnClause() { - return jpaProvider.needsAssociationToIdRewriteInOnClause(); - } - @Override public boolean needsBrokenAssociationToIdRewriteInOnClause() { return jpaProvider.needsBrokenAssociationToIdRewriteInOnClause(); diff --git a/core/impl/src/main/java/com/blazebit/persistence/impl/ResolvingQueryGenerator.java b/core/impl/src/main/java/com/blazebit/persistence/impl/ResolvingQueryGenerator.java index de2842bff7..2eec5c940e 100644 --- a/core/impl/src/main/java/com/blazebit/persistence/impl/ResolvingQueryGenerator.java +++ b/core/impl/src/main/java/com/blazebit/persistence/impl/ResolvingQueryGenerator.java @@ -292,7 +292,9 @@ protected boolean isSimpleSubquery(SubqueryExpression expression) { final boolean hasLimit = hasFirstResult || hasMaxResults; final boolean hasSetOperations = subquery instanceof BaseFinalSetOperationBuilder; final boolean hasEntityFunctions = subquery.joinManager.hasEntityFunctions(); - return !hasLimit && !hasSetOperations && !hasEntityFunctions && !subquery.joinManager.hasLateInlineNodes(); + return (jpaProvider.supportsSubqueryLimitOffset() || !hasLimit) + && (jpaProvider.supportsSetOperations() || !hasSetOperations) + && !hasEntityFunctions && !subquery.joinManager.hasLateInlineNodes(); } return super.isSimpleSubquery(expression); } @@ -885,37 +887,22 @@ public void visit(ArrayExpression expression) { public void visit(InPredicate predicate) { boolean quantifiedPredicate = this.quantifiedPredicate; this.quantifiedPredicate = true; - if (predicate.getRight().size() == 1 && jpaProvider.needsAssociationToIdRewriteInOnClause() && clauseType == ClauseType.JOIN) { - Expression right = predicate.getRight().get(0); - if (right instanceof ParameterExpression) { - ParameterExpression parameterExpression = (ParameterExpression) right; - @SuppressWarnings("unchecked") - Type associationType = getAssociationType(predicate.getLeft(), right); - // If the association type is a entity type, we transform it - if (associationType instanceof EntityType) { - renderEquality(predicate.getLeft(), right, predicate.isNegated(), PredicateQuantifier.ONE); - } else { - super.visit(predicate); - } - } else if (right instanceof PathExpression) { - renderEquality(predicate.getLeft(), right, predicate.isNegated(), PredicateQuantifier.ONE); - } else { - super.visit(predicate); - } + Expression right; + SubqueryExpression subqueryExpression; + if (!externalRepresentation + && predicate.getRight().size() == 1 && (right = predicate.getRight().get(0)) instanceof SubqueryExpression + && (subqueryExpression = (SubqueryExpression) right).getSubquery() instanceof SubqueryInternalBuilder + && ((SubqueryInternalBuilder) subqueryExpression.getSubquery()).getMaxResults() == 1) { + // This is a special rewrite for databases like e.g. MySQL + predicate.getLeft().accept(this); + sb.append(predicate.isNegated() ? " <> " : " = "); + right.accept(this); } else { super.visit(predicate); } this.quantifiedPredicate = quantifiedPredicate; } - private Type getAssociationType(Expression expression1, Expression expression2) { - if (expression1 instanceof PathExpression) { - return ((PathExpression) expression1).getPathReference().getType(); - } - - return ((PathExpression) expression2).getPathReference().getType(); - } - @Override public void visit(final EqPredicate predicate) { boolean quantifiedPredicate = this.quantifiedPredicate; @@ -938,60 +925,47 @@ private void renderEquality(Expression left, Expression right, boolean negated, Expression expressionToSplit = needsEmbeddableSplitting(left, right); - if (jpaProvider.needsAssociationToIdRewriteInOnClause() && clauseType == ClauseType.JOIN) { - boolean rewritten = renderAssociationIdIfPossible(left); + if (expressionToSplit == null || !(left instanceof ParameterExpression) && !(right instanceof ParameterExpression)) { + left.accept(this); sb.append(operator); if (quantifier != PredicateQuantifier.ONE) { sb.append(quantifier.toString()); } - rewritten |= renderAssociationIdIfPossible(right); - if (rewritten) { - rewriteToIdParam(left); - rewriteToIdParam(right); - } + right.accept(this); } else { - if (expressionToSplit == null || !(left instanceof ParameterExpression) && !(right instanceof ParameterExpression)) { - left.accept(this); - sb.append(operator); - if (quantifier != PredicateQuantifier.ONE) { - sb.append(quantifier.toString()); - } - right.accept(this); + // We split the path and the parameter expression accordingly + // TODO: Try to handle map key expressions, although no JPA provider supports de-referencing map keys + PathExpression pathExpression = (PathExpression) expressionToSplit; + ParameterExpression parameterExpression; + if (left instanceof ParameterExpression) { + parameterExpression = (ParameterExpression) left; } else { - // We split the path and the parameter expression accordingly - // TODO: Try to handle map key expressions, although no JPA provider supports de-referencing map keys - PathExpression pathExpression = (PathExpression) expressionToSplit; - ParameterExpression parameterExpression; - if (left instanceof ParameterExpression) { - parameterExpression = (ParameterExpression) left; - } else { - parameterExpression = (ParameterExpression) right; + parameterExpression = (ParameterExpression) right; + } + PathReference pathReference = pathExpression.getPathReference(); + EmbeddableType embeddableType = (EmbeddableType) pathReference.getType(); + String parameterName = parameterExpression.getName(); + Map> parameterAccessPaths = new HashMap<>(); + ParameterManager.ParameterImpl parameter = parameterManager.getParameter(parameterName); + sb.append('('); + for (Attribute attribute : embeddableType.getAttributes()) { + ((JoinNode) pathReference.getBaseNode()).appendDeReference(sb, pathReference.getField() + "." + attribute.getName(), externalRepresentation); + String embeddedPropertyName = attribute.getName(); + String subParamName = "_" + parameterName + "_" + embeddedPropertyName.replace('.', '_'); + sb.append(operator); + sb.append(":").append(subParamName); + if (parameter.getTransformer() == null) { + parameterManager.registerParameterName(subParamName, false, null, null); } - PathReference pathReference = pathExpression.getPathReference(); - EmbeddableType embeddableType = (EmbeddableType) pathReference.getType(); - String parameterName = parameterExpression.getName(); - Map> parameterAccessPaths = new HashMap<>(); - ParameterManager.ParameterImpl parameter = parameterManager.getParameter(parameterName); - sb.append('('); - for (Attribute attribute : embeddableType.getAttributes()) { - ((JoinNode) pathReference.getBaseNode()).appendDeReference(sb, pathReference.getField() + "." + attribute.getName(), externalRepresentation); - String embeddedPropertyName = attribute.getName(); - String subParamName = "_" + parameterName + "_" + embeddedPropertyName.replace('.', '_'); - sb.append(operator); - sb.append(":").append(subParamName); - if (parameter.getTransformer() == null) { - parameterManager.registerParameterName(subParamName, false, null, null); - } - parameterAccessPaths.put(subParamName, Arrays.asList(embeddedPropertyName.split("\\."))); + parameterAccessPaths.put(subParamName, Arrays.asList(embeddedPropertyName.split("\\."))); - sb.append(" AND "); - } - sb.setLength(sb.length() - " AND ".length()); - sb.append(')'); + sb.append(" AND "); + } + sb.setLength(sb.length() - " AND ".length()); + sb.append(')'); - if (parameter.getTransformer() == null) { - parameter.setTransformer(new SplittingParameterTransformer(parameterManager, entityMetamodel, embeddableType.getJavaType(), parameterAccessPaths)); - } + if (parameter.getTransformer() == null) { + parameter.setTransformer(new SplittingParameterTransformer(parameterManager, entityMetamodel, embeddableType.getJavaType(), parameterAccessPaths)); } } setBooleanLiteralRenderingContext(oldBooleanLiteralRenderingContext); diff --git a/integration/eclipselink/src/main/java/com/blazebit/persistence/integration/eclipselink/EclipseLinkJpaProvider.java b/integration/eclipselink/src/main/java/com/blazebit/persistence/integration/eclipselink/EclipseLinkJpaProvider.java index c4e97021e5..cf15438598 100644 --- a/integration/eclipselink/src/main/java/com/blazebit/persistence/integration/eclipselink/EclipseLinkJpaProvider.java +++ b/integration/eclipselink/src/main/java/com/blazebit/persistence/integration/eclipselink/EclipseLinkJpaProvider.java @@ -618,11 +618,6 @@ public boolean supportsTransientEntityAsParameter() { return true; } - @Override - public boolean needsAssociationToIdRewriteInOnClause() { - return false; - } - @Override public boolean needsBrokenAssociationToIdRewriteInOnClause() { return false; diff --git a/integration/hibernate6-base/src/main/java/com/blazebit/persistence/integration/hibernate/base/HibernateJpaProvider.java b/integration/hibernate6-base/src/main/java/com/blazebit/persistence/integration/hibernate/base/HibernateJpaProvider.java index 93fe562d99..2d5ae35992 100644 --- a/integration/hibernate6-base/src/main/java/com/blazebit/persistence/integration/hibernate/base/HibernateJpaProvider.java +++ b/integration/hibernate6-base/src/main/java/com/blazebit/persistence/integration/hibernate/base/HibernateJpaProvider.java @@ -129,11 +129,6 @@ public boolean supportsForeignAssociationInOnClause() { return true; } - @Override - public boolean needsAssociationToIdRewriteInOnClause() { - return false; - } - @Override public boolean needsBrokenAssociationToIdRewriteInOnClause() { return false;