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:
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user