Skip to content

Commit d201bcf

Browse files
Fix issue with collection types directly referenced on the root entity
1 parent 0079908 commit d201bcf

File tree

2 files changed

+83
-0
lines changed

2 files changed

+83
-0
lines changed

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import jakarta.persistence.metamodel.Attribute;
1919
import jakarta.persistence.metamodel.Bindable;
20+
import jakarta.persistence.metamodel.EntityType;
2021
import jakarta.persistence.metamodel.ManagedType;
2122
import jakarta.persistence.metamodel.Metamodel;
2223

@@ -85,6 +86,11 @@ public JpqlQueryBuilder.PathExpression toExpressionRecursively(Metamodel metamod
8586

8687
// if it's a leaf, return the join
8788
if (isLeafProperty) {
89+
90+
// except its a collection type on the root
91+
if (from instanceof EntityType<?> && property.isCollection()) {
92+
return new JpqlQueryBuilder.PathAndOrigin(property, source, false);
93+
}
8894
return new JpqlQueryBuilder.PathAndOrigin(property, joinSource, true);
8995
}
9096

spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpqlQueryBuilderUnitTests.java

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,67 @@ void referencesCollectionViaJoin() {
264264
"SELECT r FROM JpqlQueryBuilderUnitTests$Race r LEFT JOIN r.lineup l WHERE l.groups IS NOT EMPTY");
265265
}
266266

267+
@Test // GH-4110
268+
void referencesCollectionViaMultipleJoins() {
269+
270+
TestMetaModel model = TestMetaModel.hibernateModel(TestUser.class, TestRole.class);
271+
Entity entity = entity(TestUser.class);
272+
273+
EntityType<TestUser> entityType = model.entity(TestUser.class);
274+
JpqlQueryBuilder.PathExpression pas = JpqlUtils.toExpressionRecursively(model, entity, entityType,
275+
PropertyPath.from("manager.colleagues.roles", TestUser.class));
276+
String jpql = JpqlQueryBuilder.selectFrom(entity).entity().where(JpqlQueryBuilder.where(pas).isEmpty()).render();
277+
278+
assertThat(jpql).isEqualTo(
279+
"SELECT t FROM JpqlQueryBuilderUnitTests$TestUser t LEFT JOIN t.manager m LEFT JOIN m.colleagues c WHERE c.roles IS EMPTY");
280+
}
281+
282+
@Test // GH-4110
283+
void referencesCollectionViaJoinWithMemberOf() {
284+
285+
TestMetaModel model = TestMetaModel.hibernateModel(TestUser.class, TestRole.class);
286+
Entity entity = entity(TestUser.class);
287+
288+
EntityType<TestUser> entityType = model.entity(TestUser.class);
289+
JpqlQueryBuilder.PathExpression pas = JpqlUtils.toExpressionRecursively(model, entity, entityType,
290+
PropertyPath.from("colleagues.roles", TestUser.class));
291+
String jpql = JpqlQueryBuilder.selectFrom(entity).entity()
292+
.where(JpqlQueryBuilder.where(pas).memberOf(JpqlQueryBuilder.parameter("?1"))).render();
293+
294+
assertThat(jpql).isEqualTo(
295+
"SELECT t FROM JpqlQueryBuilderUnitTests$TestUser t LEFT JOIN t.colleagues c WHERE ?1 MEMBER OF c.roles");
296+
}
297+
298+
@Test // GH-4110
299+
void directCollectionPropertyDoesNotCreateJoin() {
300+
301+
TestMetaModel model = TestMetaModel.hibernateModel(TestUser.class, TestRole.class);
302+
Entity entity = entity(TestUser.class);
303+
304+
EntityType<TestUser> entityType = model.entity(TestUser.class);
305+
JpqlQueryBuilder.PathExpression pas = JpqlUtils.toExpressionRecursively(model, entity, entityType,
306+
PropertyPath.from("roles", TestUser.class));
307+
String jpql = JpqlQueryBuilder.selectFrom(entity).entity().where(JpqlQueryBuilder.where(pas).isEmpty()).render();
308+
309+
assertThat(jpql).isEqualTo("SELECT t FROM JpqlQueryBuilderUnitTests$TestUser t WHERE t.roles IS EMPTY");
310+
}
311+
312+
@Test // GH-4110
313+
void collectionWithAdditionalPathSegments() {
314+
315+
TestMetaModel model = TestMetaModel.hibernateModel(TestUser.class, TestRole.class);
316+
Entity entity = entity(TestUser.class);
317+
318+
EntityType<TestUser> entityType = model.entity(TestUser.class);
319+
JpqlQueryBuilder.PathExpression pas = JpqlUtils.toExpressionRecursively(model, entity, entityType,
320+
PropertyPath.from("colleagues.roles.name", TestUser.class));
321+
String jpql = JpqlQueryBuilder.selectFrom(entity).entity()
322+
.where(JpqlQueryBuilder.where(pas).eq(literal("ADMIN"))).render();
323+
324+
assertThat(jpql).isEqualTo(
325+
"SELECT t FROM JpqlQueryBuilderUnitTests$TestUser t LEFT JOIN t.colleagues c LEFT JOIN c.roles r WHERE r.name = 'ADMIN'");
326+
}
327+
267328
static ContextualAssert contextual(RenderContext context) {
268329
return new ContextualAssert(context);
269330
}
@@ -385,4 +446,20 @@ static class GroupId {
385446

386447
}
387448

449+
@jakarta.persistence.Entity
450+
static class TestUser {
451+
452+
@Id long id;
453+
@ManyToOne TestUser manager;
454+
@OneToMany Set<TestUser> colleagues = new HashSet<>();
455+
@OneToMany Set<TestRole> roles = new HashSet<>();
456+
}
457+
458+
@jakarta.persistence.Entity
459+
static class TestRole {
460+
461+
@Id long id;
462+
String name;
463+
}
464+
388465
}

0 commit comments

Comments
 (0)