Skip to content

Commit

Permalink
Workaround a MySQL limitation and cleanup JpaProvider
Browse files Browse the repository at this point in the history
  • Loading branch information
beikov committed Nov 9, 2024
1 parent aeecb32 commit 4820390
Show file tree
Hide file tree
Showing 6 changed files with 46 additions and 96 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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 <code>alias.association</code> in the ON clause is rewritten to
* <code>alias.association.id</code>.
*
* @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 <code>alias.association</code> in the ON clause is rewritten to
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -333,11 +333,6 @@ public boolean supportsTransientEntityAsParameter() {
return jpaProvider.supportsTransientEntityAsParameter();
}

@Override
public boolean needsAssociationToIdRewriteInOnClause() {
return jpaProvider.needsAssociationToIdRewriteInOnClause();
}

@Override
public boolean needsBrokenAssociationToIdRewriteInOnClause() {
return jpaProvider.needsBrokenAssociationToIdRewriteInOnClause();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down Expand Up @@ -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;
Expand All @@ -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<String, List<String>> 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<String, List<String>> 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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -618,11 +618,6 @@ public boolean supportsTransientEntityAsParameter() {
return true;
}

@Override
public boolean needsAssociationToIdRewriteInOnClause() {
return false;
}

@Override
public boolean needsBrokenAssociationToIdRewriteInOnClause() {
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,11 +129,6 @@ public boolean supportsForeignAssociationInOnClause() {
return true;
}

@Override
public boolean needsAssociationToIdRewriteInOnClause() {
return false;
}

@Override
public boolean needsBrokenAssociationToIdRewriteInOnClause() {
return false;
Expand Down

0 comments on commit 4820390

Please sign in to comment.