Skip to content

Commit 159ecdf

Browse files
committed
Fix selection with outer joins.
1 parent 9ebfe27 commit 159ecdf

File tree

2 files changed

+39
-26
lines changed

2 files changed

+39
-26
lines changed

spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,10 @@
2222
import jakarta.persistence.criteria.Expression;
2323
import jakarta.persistence.criteria.From;
2424
import jakarta.persistence.criteria.Predicate;
25-
import jakarta.persistence.metamodel.Attribute;
2625
import jakarta.persistence.metamodel.EntityType;
2726
import jakarta.persistence.metamodel.SingularAttribute;
2827

28+
import java.util.ArrayList;
2929
import java.util.Collection;
3030
import java.util.Collections;
3131
import java.util.Iterator;
@@ -210,10 +210,18 @@ private JpqlQueryBuilder.Select doSelect(Sort sort) {
210210
if (returnedType.needsCustomConstruction()) {
211211

212212
Collection<String> requiredSelection = getRequiredSelection(sort, returnedType);
213+
214+
List<PathAndOrigin> paths = new ArrayList<>(requiredSelection.size());
215+
for (String selection : requiredSelection) {
216+
paths.add(
217+
JpqlUtils.toExpressionRecursively(entity, from, PropertyPath.from(selection, from.getJavaType()), true));
218+
}
219+
213220
if (useTupleQuery()) {
214-
return selectStep.select(requiredSelection);
221+
222+
return selectStep.select(paths);
215223
} else {
216-
return selectStep.instantiate(returnedType.getReturnedType(), requiredSelection);
224+
return selectStep.instantiate(returnedType.getReturnedType(), paths);
217225
}
218226
}
219227

@@ -222,12 +230,16 @@ private JpqlQueryBuilder.Select doSelect(Sort sort) {
222230
if (entityType.hasSingleIdAttribute()) {
223231

224232
SingularAttribute<?, ?> id = entityType.getId(entityType.getIdType().getJavaType());
225-
return selectStep.select(id.getName());
233+
return selectStep.select(
234+
JpqlUtils.toExpressionRecursively(entity, from, PropertyPath.from(id.getName(), from.getJavaType()), true));
226235

227236
} else {
228237

229-
return selectStep.select(entityType.getIdClassAttributes().stream()//
230-
.map(Attribute::getName).toList());
238+
List<PathAndOrigin> paths = entityType.getIdClassAttributes().stream()//
239+
.map(it -> JpqlUtils.toExpressionRecursively(entity, from,
240+
PropertyPath.from(it.getName(), from.getJavaType()), true))
241+
.toList();
242+
return selectStep.select(paths);
231243
}
232244
}
233245

spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpqlQueryBuilder.java

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -118,12 +118,12 @@ public Select count() {
118118
}
119119

120120
@Override
121-
public Select instantiate(String resultType, Collection<String> paths) {
121+
public Select instantiate(String resultType, Collection<PathAndOrigin> paths) {
122122
return new Select(postProcess(new ConstructorExpression(resultType, new Multiselect(from, paths))), from);
123123
}
124124

125125
@Override
126-
public Select select(Collection<String> paths) {
126+
public Select select(Collection<PathAndOrigin> paths) {
127127
return new Select(postProcess(new Multiselect(from, paths)), from);
128128
}
129129

@@ -411,36 +411,35 @@ public interface SelectStep {
411411
* @param paths
412412
* @return
413413
*/
414-
default Select instantiate(Class<?> resultType, Collection<String> paths) {
414+
default Select instantiate(Class<?> resultType, Collection<PathAndOrigin> paths) {
415415
return instantiate(resultType.getName(), paths);
416416
}
417417

418418
/**
419-
* Provide a constructor expression to instantiate {@code resultType}. Operates on the underlying {@link Entity
420-
* FROM}.
419+
* Provide a constructor expression to instantiate {@code resultType}.
421420
*
422421
* @param resultType
423422
* @param paths
424423
* @return
425424
*/
426-
Select instantiate(String resultType, Collection<String> paths);
425+
Select instantiate(String resultType, Collection<PathAndOrigin> paths);
427426

428427
/**
429-
* Specify a multi-select. Operates on the underlying {@link Entity FROM}.
428+
* Specify a multi-select.
430429
*
431430
* @param paths
432431
* @return
433432
*/
434-
Select select(Collection<String> paths);
433+
Select select(Collection<PathAndOrigin> paths);
435434

436435
/**
437-
* Select a single attribute. Operates on the underlying {@link Entity FROM}.
436+
* Select a single attribute.
438437
*
439438
* @param name
440439
* @return
441440
*/
442-
default Select select(String name) {
443-
return select(List.of(name));
441+
default Select select(PathAndOrigin path) {
442+
return select(List.of(path));
444443
}
445444

446445
}
@@ -529,24 +528,21 @@ public String toString() {
529528
* @param source
530529
* @param paths
531530
*/
532-
record Multiselect(Origin source, Collection<String> paths) implements Selection {
531+
record Multiselect(Origin source, Collection<PathAndOrigin> paths) implements Selection {
533532

534533
@Override
535534
public String render(RenderContext context) {
536535

537536
StringBuilder builder = new StringBuilder();
538537

539-
for (String path : paths) {
538+
for (PathAndOrigin path : paths) {
540539

541540
if (!builder.isEmpty()) {
542541
builder.append(", ");
543542
}
544543

545-
builder.append(context.prefixWithAlias(source, path));
546-
547-
if (!path.contains(".")) {
548-
builder.append(" ").append(path);
549-
}
544+
builder.append(PathExpression.render(path, context));
545+
builder.append(" ").append(path.path().getSegment());
550546
}
551547

552548
return builder.toString();
@@ -1023,11 +1019,16 @@ record PathExpression(PathAndOrigin pas) implements Expression {
10231019

10241020
@Override
10251021
public String render(RenderContext context) {
1022+
return render(pas, context);
1023+
1024+
}
1025+
1026+
public static String render(PathAndOrigin pas, RenderContext context) {
10261027

10271028
if (pas.path().hasNext() || !pas.onTheJoin()) {
1028-
return context.prefixWithAlias(pas().origin(), pas.path().toDotPath());
1029+
return context.prefixWithAlias(pas.origin(), pas.path().toDotPath());
10291030
} else {
1030-
return context.getAlias(pas().origin());
1031+
return context.getAlias(pas.origin());
10311032
}
10321033
}
10331034

0 commit comments

Comments
 (0)