From bd85e9b10c1a3d0f0419a69c6e87aa60f67266b1 Mon Sep 17 00:00:00 2001 From: Oliver Drotbohm Date: Tue, 25 Jan 2022 09:17:06 +0100 Subject: [PATCH 1/4] Polishing. Indentation in testcases (spaces -> tabs). Additional imports. --- .../data/jpa/repository/UserRepositoryTests.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java index fcf384fac7..6da910e7ba 100644 --- a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java @@ -56,6 +56,8 @@ import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.domain.Example; import org.springframework.data.domain.ExampleMatcher; +import org.springframework.data.domain.ExampleMatcher.GenericPropertyMatcher; +import org.springframework.data.domain.ExampleMatcher.StringMatcher; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.PageRequest; From ea5b214148432daa6bde6ab16ec1c48bb04f74fa Mon Sep 17 00:00:00 2001 From: "Greg L. Turnquist" Date: Fri, 7 Jan 2022 09:25:19 -0600 Subject: [PATCH 2/4] Ignore fields with Optional.empty when performing Query by Example. The Auditable interface introduces Optional getters, which when combined with Query by Example results in cryptic errors. By ignoring a probe's field that contains an Optional.empty, Query by Example works properly. NOTE: This fix actually tests outside the originally detected scope of Auditable, verifying that ALL Optional.empty() fields are properly handled. Closes #2176 Original pull request #2401 --- .../domain/sample/UserWithOptionalField.java | 60 +++++++++++++++ .../UserWithOptionalFieldRepository.java | 23 ++++++ .../QueryByExampleWithOptionalEmptyTests.java | 73 +++++++++++++++++++ 3 files changed, 156 insertions(+) create mode 100644 src/test/java/org/springframework/data/jpa/domain/sample/UserWithOptionalField.java create mode 100644 src/test/java/org/springframework/data/jpa/domain/sample/UserWithOptionalFieldRepository.java create mode 100644 src/test/java/org/springframework/data/jpa/domain/support/QueryByExampleWithOptionalEmptyTests.java diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/UserWithOptionalField.java b/src/test/java/org/springframework/data/jpa/domain/sample/UserWithOptionalField.java new file mode 100644 index 0000000000..0c8f7593fb --- /dev/null +++ b/src/test/java/org/springframework/data/jpa/domain/sample/UserWithOptionalField.java @@ -0,0 +1,60 @@ +/* + * Copyright 2008-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.domain.sample; + +import lombok.Data; + +import java.util.Optional; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; + +import org.springframework.lang.Nullable; + +/** + * @author Greg Turnquist + */ +@Entity +@Data +public class UserWithOptionalField { + + @Id @GeneratedValue private Long id; + private String name; + private String role; + + public UserWithOptionalField() { + + this.id = null; + this.name = null; + this.role = null; + } + + public UserWithOptionalField(String name, @Nullable String role) { + + this(); + this.name = name; + this.role = role; + } + + public Optional getRole() { + return Optional.ofNullable(this.role); + } + + public void setRole(Optional role) { + this.role = role.orElse(null); + } +} diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/UserWithOptionalFieldRepository.java b/src/test/java/org/springframework/data/jpa/domain/sample/UserWithOptionalFieldRepository.java new file mode 100644 index 0000000000..edf5f9d2ea --- /dev/null +++ b/src/test/java/org/springframework/data/jpa/domain/sample/UserWithOptionalFieldRepository.java @@ -0,0 +1,23 @@ +/* + * Copyright 2008-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.domain.sample; + +import org.springframework.data.jpa.repository.JpaRepository; + +/** + * @author Greg Turnquist + */ +public interface UserWithOptionalFieldRepository extends JpaRepository {} diff --git a/src/test/java/org/springframework/data/jpa/domain/support/QueryByExampleWithOptionalEmptyTests.java b/src/test/java/org/springframework/data/jpa/domain/support/QueryByExampleWithOptionalEmptyTests.java new file mode 100644 index 0000000000..59d6080227 --- /dev/null +++ b/src/test/java/org/springframework/data/jpa/domain/support/QueryByExampleWithOptionalEmptyTests.java @@ -0,0 +1,73 @@ +/* + * Copyright 2008-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.domain.support; + +import static org.assertj.core.api.Assertions.*; + +import java.util.List; +import java.util.Optional; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.ImportResource; +import org.springframework.data.domain.Example; +import org.springframework.data.jpa.domain.sample.UserWithOptionalField; +import org.springframework.data.jpa.domain.sample.UserWithOptionalFieldRepository; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +/** + * Integration test for {@link org.springframework.data.repository.query.QueryByExampleExecutor} involving + * {@link Optional#empty()}. + * + * @author Greg Turnquist + */ +@ExtendWith(SpringExtension.class) +@ContextConfiguration +public class QueryByExampleWithOptionalEmptyTests { + + @Autowired UserWithOptionalFieldRepository repository; + UserWithOptionalField user; + + @Test + void queryByExampleTreatsEmptyOptionalsLikeNulls() { + + // given + UserWithOptionalField user = new UserWithOptionalField(); + user.setName("Greg"); + repository.saveAndFlush(user); + + // when + UserWithOptionalField probe = new UserWithOptionalField(); + probe.setName("Greg"); + Example example = Example.of(probe); + + // then + List results = repository.findAll(example); + + assertThat(results).hasSize(1); + assertThat(results).extracting(UserWithOptionalField::getName).containsExactly("Greg"); + } + + @Configuration + @EnableJpaRepositories(basePackageClasses = UserWithOptionalFieldRepository.class) + @ImportResource("classpath:infrastructure.xml") + static class JpaRepositoryConfig {} + +} From 7cbfad970de669c663712c5af321f572a4277dce Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Fri, 4 Feb 2022 13:51:47 +0100 Subject: [PATCH 3/4] Polishing. Removed Given/When/Then comments. They are of limited use, especially since it is debatable what belongs in which section. See #2176 Original pull request #2401 --- .../domain/support/QueryByExampleWithOptionalEmptyTests.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/test/java/org/springframework/data/jpa/domain/support/QueryByExampleWithOptionalEmptyTests.java b/src/test/java/org/springframework/data/jpa/domain/support/QueryByExampleWithOptionalEmptyTests.java index 59d6080227..23bc7a115a 100644 --- a/src/test/java/org/springframework/data/jpa/domain/support/QueryByExampleWithOptionalEmptyTests.java +++ b/src/test/java/org/springframework/data/jpa/domain/support/QueryByExampleWithOptionalEmptyTests.java @@ -48,17 +48,14 @@ public class QueryByExampleWithOptionalEmptyTests { @Test void queryByExampleTreatsEmptyOptionalsLikeNulls() { - // given UserWithOptionalField user = new UserWithOptionalField(); user.setName("Greg"); repository.saveAndFlush(user); - // when UserWithOptionalField probe = new UserWithOptionalField(); probe.setName("Greg"); Example example = Example.of(probe); - // then List results = repository.findAll(example); assertThat(results).hasSize(1); From 1d1be8285c532dbd53c3cb7db2785abe17749cc9 Mon Sep 17 00:00:00 2001 From: Anton Molganov Date: Wed, 9 Feb 2022 00:41:00 +0500 Subject: [PATCH 4/4] Added query fetches search for join existence when applying JPA ordering. Closes #2253 See https://github.com/spring-projects/spring-data-jpa/issues/2253 --- .../data/jpa/repository/query/QueryUtils.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java index eba4466bb7..3670c2f1f0 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java @@ -76,6 +76,7 @@ * @author Greg Turnquist * @author Diego Krupitza * @author Jędrzej Biedrzycki + * @author Anton Molganov */ public abstract class QueryUtils { @@ -806,6 +807,16 @@ private static T getAnnotationProperty(Attribute attribute, String pro return join; } } + + for (Fetch fetch : from.getFetches()) { + + Join join = (Join) fetch; + if (joinType == join.getJoinType() + && join.getAttribute().getName().equals(attribute)) { + return join; + } + } + return from.join(attribute, joinType); }