Make getPredicate return null when there are no predicates.

This does integration testing with both EclipseLink and Hibernate, verifying that queries are run properly. I also inspected the generated queries in the debugger, verifying the "where true=1" was no longer generated.

Closes #2282.
This commit is contained in:
Greg L. Turnquist
2022-04-12 16:13:51 -05:00
parent f5ddb7aa32
commit 724538999a
5 changed files with 205 additions and 36 deletions

View File

@@ -15,12 +15,6 @@
*/
package org.springframework.data.jpa.convert;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.Expression;
import jakarta.persistence.criteria.From;
@@ -32,6 +26,12 @@ import jakarta.persistence.metamodel.Attribute.PersistentAttributeType;
import jakarta.persistence.metamodel.ManagedType;
import jakarta.persistence.metamodel.SingularAttribute;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.ExampleMatcher;
@@ -76,8 +76,9 @@ public class QueryByExamplePredicateBuilder {
* @param root must not be {@literal null}.
* @param cb must not be {@literal null}.
* @param example must not be {@literal null}.
* @return never {@literal null}.
* @return {@literal null} indicates no {@link Predicate}.
*/
@Nullable
public static <T> Predicate getPredicate(Root<T> root, CriteriaBuilder cb, Example<T> example) {
return getPredicate(root, cb, example, EscapeCharacter.DEFAULT);
}
@@ -89,8 +90,9 @@ public class QueryByExamplePredicateBuilder {
* @param cb must not be {@literal null}.
* @param example must not be {@literal null}.
* @param escapeCharacter Must not be {@literal null}.
* @return never {@literal null}.
* @return {@literal null} indicates no constraints
*/
@Nullable
public static <T> Predicate getPredicate(Root<T> root, CriteriaBuilder cb, Example<T> example,
EscapeCharacter escapeCharacter) {
@@ -105,7 +107,7 @@ public class QueryByExamplePredicateBuilder {
escapeCharacter);
if (predicates.isEmpty()) {
return cb.isTrue(cb.literal(true));
return null;
}
if (predicates.size() == 1) {

View File

@@ -20,10 +20,6 @@ import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
import static org.springframework.data.domain.Example.*;
import java.lang.reflect.Member;
import java.util.LinkedHashSet;
import java.util.Set;
import jakarta.persistence.Id;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.Expression;
@@ -37,6 +33,10 @@ import jakarta.persistence.metamodel.ManagedType;
import jakarta.persistence.metamodel.SingularAttribute;
import jakarta.persistence.metamodel.Type;
import java.lang.reflect.Member;
import java.util.LinkedHashSet;
import java.util.Set;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@@ -45,7 +45,6 @@ import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.quality.Strictness;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.ExampleMatcher;
import org.springframework.data.domain.ExampleMatcher.GenericPropertyMatcher;
@@ -90,19 +89,16 @@ class QueryByExamplePredicateBuilderUnitTests {
void setUp() {
personIdAttribute = new SingularAttributeStub<>("id", PersistentAttributeType.BASIC, Long.class);
personFirstnameAttribute = new SingularAttributeStub<>("firstname", PersistentAttributeType.BASIC,
String.class);
personFirstnameAttribute = new SingularAttributeStub<>("firstname", PersistentAttributeType.BASIC, String.class);
personAgeAttribute = new SingularAttributeStub<>("age", PersistentAttributeType.BASIC, Long.class);
personFatherAttribute = new SingularAttributeStub<>("father", PersistentAttributeType.MANY_TO_ONE,
Person.class, personEntityType);
personSkillAttribute = new SingularAttributeStub<>("skill", PersistentAttributeType.EMBEDDED,
Skill.class, skillEntityType);
personAddressAttribute = new SingularAttributeStub<>("address", PersistentAttributeType.EMBEDDED,
Address.class);
skillNameAttribute = new SingularAttributeStub<>("name", PersistentAttributeType.BASIC,
String.class);
skillNestedAttribute = new SingularAttributeStub<>("nested", PersistentAttributeType.MANY_TO_ONE,
Skill.class, skillEntityType);
personFatherAttribute = new SingularAttributeStub<>("father", PersistentAttributeType.MANY_TO_ONE, Person.class,
personEntityType);
personSkillAttribute = new SingularAttributeStub<>("skill", PersistentAttributeType.EMBEDDED, Skill.class,
skillEntityType);
personAddressAttribute = new SingularAttributeStub<>("address", PersistentAttributeType.EMBEDDED, Address.class);
skillNameAttribute = new SingularAttributeStub<>("name", PersistentAttributeType.BASIC, String.class);
skillNestedAttribute = new SingularAttributeStub<>("nested", PersistentAttributeType.MANY_TO_ONE, Skill.class,
skillEntityType);
personEntityAttribtues = new LinkedHashSet<>();
personEntityAttribtues.add(personIdAttribute);
@@ -153,9 +149,9 @@ class QueryByExamplePredicateBuilderUnitTests {
}
@Test // DATAJPA-218
void emptyCriteriaListShouldResultTruePredicate() {
void emptyCriteriaListShouldResultInNullPredicate() {
assertThat(QueryByExamplePredicateBuilder.getPredicate(root, cb, of(new Person()), EscapeCharacter.DEFAULT))
.isEqualTo(truePredicate);
.isNull();
}
@Test // DATAJPA-218
@@ -306,13 +302,11 @@ class QueryByExamplePredicateBuilderUnitTests {
private Class<T> javaType;
private Type<T> type;
SingularAttributeStub(String name,
jakarta.persistence.metamodel.Attribute.PersistentAttributeType attributeType, Class<T> javaType) {
SingularAttributeStub(String name, PersistentAttributeType attributeType, Class<T> javaType) {
this(name, attributeType, javaType, null);
}
SingularAttributeStub(String name,
jakarta.persistence.metamodel.Attribute.PersistentAttributeType attributeType, Class<T> javaType, Type<T> type) {
SingularAttributeStub(String name, PersistentAttributeType attributeType, Class<T> javaType, Type<T> type) {
this.name = name;
this.attributeType = attributeType;
this.javaType = javaType;
@@ -325,7 +319,7 @@ class QueryByExamplePredicateBuilderUnitTests {
}
@Override
public jakarta.persistence.metamodel.Attribute.PersistentAttributeType getPersistentAttributeType() {
public PersistentAttributeType getPersistentAttributeType() {
return attributeType;
}

View File

@@ -0,0 +1,86 @@
/*
* 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.repository;
import static org.assertj.core.api.Assertions.*;
import jakarta.persistence.EntityManager;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Predicate;
import jakarta.persistence.criteria.Root;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import org.springframework.data.jpa.convert.QueryByExamplePredicateBuilder;
import org.springframework.data.jpa.domain.sample.Role;
import org.springframework.data.jpa.repository.sample.RoleRepository;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.transaction.annotation.Transactional;
/**
* @author Greg Turnquist
* @since 3.0
*/
@ExtendWith(SpringExtension.class)
@ContextConfiguration({ "classpath:eclipselink.xml", "classpath:config/namespace-application-context.xml" })
@Transactional
public class QueryByExampleEclipseLinkIntegrationTests {
@Autowired RoleRepository repository;
@Autowired EntityManager em;
private Role drummer;
private Role guitarist;
private Role singer;
@BeforeEach
void setUp() {
drummer = repository.save(new Role("drummer"));
guitarist = repository.save(new Role("guitarist"));
singer = repository.save(new Role("singer"));
}
@AfterEach
void clearUp() {
repository.deleteAll();
}
@Test // GH-2283
void queryByExampleWithNoPredicatesShouldHaveNoWhereClause() {
// given
Role probe = new Role();
Example<Role> example = Example.of(probe);
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<Role> query = builder.createQuery(Role.class);
Root<Role> root = query.from(Role.class);
// when
Predicate predicate = QueryByExamplePredicateBuilder.getPredicate(root, builder, example);
// then
assertThat(predicate).isNull();
assertThat(repository.findAll(example)).containsExactlyInAnyOrder(drummer, guitarist, singer);
}
}

View File

@@ -0,0 +1,86 @@
/*
* 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.repository;
import static org.assertj.core.api.Assertions.*;
import jakarta.persistence.EntityManager;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Predicate;
import jakarta.persistence.criteria.Root;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import org.springframework.data.jpa.convert.QueryByExamplePredicateBuilder;
import org.springframework.data.jpa.domain.sample.Role;
import org.springframework.data.jpa.repository.sample.RoleRepository;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.transaction.annotation.Transactional;
/**
* @author Greg Turnquist
* @since 3.0
*/
@ExtendWith(SpringExtension.class)
@ContextConfiguration({ "classpath:hibernate.xml", "classpath:config/namespace-application-context.xml" })
@Transactional
public class QueryByExampleHibernateIntegrationTests {
@Autowired RoleRepository repository;
@Autowired EntityManager em;
private Role drummer;
private Role guitarist;
private Role singer;
@BeforeEach
void setUp() {
drummer = repository.save(new Role("drummer"));
guitarist = repository.save(new Role("guitarist"));
singer = repository.save(new Role("singer"));
}
@AfterEach
void clearUp() {
repository.deleteAll();
}
@Test // GH-2283
void queryByExampleWithNoPredicatesShouldHaveNoWhereClause() {
// given
Role probe = new Role();
Example<Role> example = Example.of(probe);
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<Role> query = builder.createQuery(Role.class);
Root<Role> root = query.from(Role.class);
// when
Predicate predicate = QueryByExamplePredicateBuilder.getPredicate(root, builder, example);
// then
assertThat(predicate).isNull();
assertThat(repository.findAll(example)).containsExactlyInAnyOrder(drummer, guitarist, singer);
}
}

View File

@@ -18,13 +18,14 @@ package org.springframework.data.jpa.repository.sample;
import jakarta.persistence.LockModeType;
import jakarta.persistence.QueryHint;
import java.util.List;
import java.util.Optional;
import org.springframework.data.jpa.domain.sample.Role;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Lock;
import org.springframework.data.jpa.repository.QueryHints;
import org.springframework.data.querydsl.QuerydslPredicateExecutor;
import org.springframework.data.repository.CrudRepository;
import com.querydsl.core.types.Predicate;
@@ -35,12 +36,12 @@ import com.querydsl.core.types.Predicate;
* @author Thomas Darimont
* @author Yanming Zhou
*/
public interface RoleRepository extends CrudRepository<Role, Integer>, QuerydslPredicateExecutor<Role> {
public interface RoleRepository extends JpaRepository<Role, Integer>, QuerydslPredicateExecutor<Role> {
@Override
@Lock(LockModeType.READ)
@QueryHints(@QueryHint(name = "foo", value = "bar"))
Iterable<Role> findAll();
List<Role> findAll();
@Override
@Lock(LockModeType.READ)