Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

getOrCreateJoin does not reuse fetch joins #2344

Closed
elmuerte opened this issue Oct 22, 2021 · 2 comments
Closed

getOrCreateJoin does not reuse fetch joins #2344

elmuerte opened this issue Oct 22, 2021 · 2 comments
Labels
status: waiting-for-triage An issue we've not yet triaged

Comments

@elmuerte
Copy link

private static Join<?, ?> getOrCreateJoin(From<?, ?> from, String attribute, JoinType joinType) {
for (Join<?, ?> join : from.getJoins()) {
if (join.getAttribute().getName().equals(attribute)) {
return join;
}
}
return from.join(attribute, joinType);
}

This can result in in duplicate joins. Except for being inefficient, this also has problematic side effects.

In MySQL columns on which you want to order must be included in distinct queries.

In order to do a select distinct e from Entity e order by e.other.name you must join fetch e.other. In Specification terms this fetch join can be done with the predicate

return (root, query, builder) -> {
	if (root.getJavaType().equals(query.getResultType())) {
		// Only add the join fetch when the root is part of the entity
		root.fetch(attribute, JoinType.INNER);
	}
	return null;
};

When the Orders are processed it creates another join (a non-fetch inner) which is used to perform the sorting on.
This results in a SQL query which MySQL does not accept.

JPQL created by Spring Data:

select distinct generatedAlias0
from Entity as generatedAlias0
inner join generatedAlias0.other as generatedAlias2 
inner join fetch generatedAlias0.other as generatedAlias3
order by generatedAlias2.name

Hibernate produces a SQL query similar to:

select distinct generatedAlias0.*, generatedAlias3.*
from Entity generatedAlias0
inner join Other generatedAlias2 on generatedAlias0.id=generatedAlias2.id 
inner join Other generatedAlias3 on generatedAlias0.id=generatedAlias3.id
order by generatedAlias2.name

It used to work with 2.4. I see that in #436 the logic in toExpressionRecursively was changed where it would check if it was already fetched, and thus not create a join.

if (requiresOuterJoin(propertyPathModel, model instanceof PluralAttribute, !property.hasNext(), isForSelection)
&& !isAlreadyFetched(from, segment)) {
Join<?, ?> join = getOrCreateJoin(from, segment);
return (Expression<T>) (property.hasNext() ? toExpressionRecursively(join, property.next(), isForSelection)
: join);
} else {
Path<Object> path = from.get(segment);
return (Expression<T>) (property.hasNext() ? toExpressionRecursively(path, property.next()) : path);
}

Note, isAlreadyInnerJoined does check the fetch joins.

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Oct 22, 2021
@schauder
Copy link
Contributor

That sounds like a duplicate of #2253

@elmuerte
Copy link
Author

Yes, it is the same problem. (Closing this duplicate issue.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: waiting-for-triage An issue we've not yet triaged
Projects
None yet
Development

No branches or pull requests

3 participants