Skip to content

Commit 0079908

Browse files
Reference collection via join in jpql.
1 parent a39c4c5 commit 0079908

File tree

5 files changed

+51
-2
lines changed

5 files changed

+51
-2
lines changed

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

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -481,7 +481,10 @@ public JpqlQueryBuilder.Predicate build() {
481481
case NOT_CONTAINING:
482482

483483
if (property.getLeafProperty().isCollection()) {
484-
where = JpqlQueryBuilder.where(entity, property);
484+
485+
if (!property.hasNext()) {
486+
where = JpqlQueryBuilder.where(entity, property);
487+
}
485488

486489
return type.equals(NOT_CONTAINING) ? where.notMemberOf(placeholder(provider.next(part)))
487490
: where.memberOf(placeholder(provider.next(part)));
@@ -522,7 +525,10 @@ public JpqlQueryBuilder.Predicate build() {
522525
throw new IllegalArgumentException("IsEmpty / IsNotEmpty can only be used on collection properties");
523526
}
524527

525-
where = JpqlQueryBuilder.where(entity, property);
528+
if (!property.hasNext()) {
529+
where = JpqlQueryBuilder.where(entity, property);
530+
}
531+
526532
return type.equals(IS_NOT_EMPTY) ? where.isNotEmpty() : where.isEmpty();
527533
case WITHIN:
528534
case NEAR:

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,11 @@ public JpqlQueryBuilder.PathExpression toExpressionRecursively(Metamodel metamod
9797
throw new IllegalStateException("Binding property is null");
9898
}
9999

100+
// this is a reference to a collection property (eg. for an is empty check)
101+
if (nextAttribute.isCollection() && !nextProperty.hasNext()) {
102+
return new JpqlQueryBuilder.PathAndOrigin(nextProperty, joinSource, false);
103+
}
104+
100105
return toExpressionRecursively(metamodel, joinSource, (Bindable<?>) nextAttribute, nextProperty, isForSelection,
101106
requiresOuterJoin);
102107
}

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,17 @@ void executesNotInQueryCorrectly() {
167167
assertThat(result).containsExactly(oliver);
168168
}
169169

170+
@Test // GH-4110
171+
void executesQueryWithEmptyOnCollection() {
172+
173+
dave.addColleague(oliver);
174+
userRepository.save(dave);
175+
userRepository.save(oliver);
176+
177+
assertThat(userRepository.findByColleaguesRolesIsEmpty()).containsExactly(dave, carter);
178+
assertThat(userRepository.findByColleaguesRolesIsNotEmpty()).containsExactlyInAnyOrder(oliver);
179+
}
180+
170181
@Test // DATAJPA-92
171182
void findsByLastnameIgnoringCase() {
172183

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

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,22 @@ void shouldRenderJoinsWithSamePathSegmentCorrectly() {
248248

249249
}
250250

251+
@Test // GH-4110
252+
void referencesCollectionViaJoin() {
253+
254+
TestMetaModel model = TestMetaModel.hibernateModel(Race.class, Groups.class, Group.class, GroupId.class,
255+
Person.class);
256+
Entity entity = entity(Race.class);
257+
258+
EntityType<Race> entityType = model.entity(Race.class);
259+
JpqlQueryBuilder.PathExpression pas = JpqlUtils.toExpressionRecursively(model, entity, entityType,
260+
PropertyPath.from("lineup.groups", Race.class));
261+
String jpql = JpqlQueryBuilder.selectFrom(entity).entity().where(JpqlQueryBuilder.where(pas).isNotEmpty()).render();
262+
263+
assertThat(jpql).isEqualTo(
264+
"SELECT r FROM JpqlQueryBuilderUnitTests$Race r LEFT JOIN r.lineup l WHERE l.groups IS NOT EMPTY");
265+
}
266+
251267
static ContextualAssert contextual(RenderContext context) {
252268
return new ContextualAssert(context);
253269
}
@@ -348,6 +364,13 @@ static class Groups {
348364

349365
}
350366

367+
@jakarta.persistence.Entity
368+
static class Race {
369+
370+
@Id long id;
371+
@OneToMany Set<Groups> lineup;
372+
}
373+
351374
@jakarta.persistence.Entity
352375
static class Group {
353376

spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,10 @@ Window<User> findTop3ByFirstnameStartingWithOrderByFirstnameAscEmailAddressAsc(S
146146

147147
List<User> findByFirstnameNotIn(Collection<String> firstnames);
148148

149+
List<User> findByColleaguesRolesIsEmpty();
150+
151+
List<User> findByColleaguesRolesIsNotEmpty();
152+
149153
// DATAJPA-292
150154
@Query("select u from User u where u.firstname like ?1%")
151155
List<User> findByFirstnameLike(String firstname);

0 commit comments

Comments
 (0)