Polishing.
Update documentation. Make sure the ParameterMetadataProivder recognises negating properties with null values as IS_NULL type. Reenabled query recreation test and add missing finder integration tests. Original Pull Request: #3681
This commit is contained in:
@@ -307,15 +307,18 @@ public class JpaQueryCreator extends AbstractQueryCreator<CriteriaQuery<? extend
|
||||
Expression<Boolean> falsePath = getTypedPath(root, part);
|
||||
return builder.isFalse(falsePath);
|
||||
case SIMPLE_PROPERTY:
|
||||
case NEGATING_SIMPLE_PROPERTY:
|
||||
|
||||
ParameterMetadata<Object> expression = provider.next(part);
|
||||
Expression<Object> path = getTypedPath(root, part);
|
||||
return expression.isIsNullParameter() ? path.isNull()
|
||||
: builder.equal(upperIfIgnoreCase(path), upperIfIgnoreCase(expression.getExpression()));
|
||||
case NEGATING_SIMPLE_PROPERTY:
|
||||
ParameterMetadata<Object> negatedExpression = provider.next(part);
|
||||
Expression<Object> negatedPath = getTypedPath(root, part);
|
||||
return negatedExpression.isIsNullParameter() ? negatedPath.isNotNull()
|
||||
: builder.notEqual(upperIfIgnoreCase(negatedPath), upperIfIgnoreCase(negatedExpression.getExpression()));
|
||||
|
||||
if (expression.isIsNullParameter()) {
|
||||
return type.equals(SIMPLE_PROPERTY) ? path.isNull() : path.isNotNull();
|
||||
} else {
|
||||
return type.equals(SIMPLE_PROPERTY)
|
||||
? builder.equal(upperIfIgnoreCase(path), upperIfIgnoreCase(expression.getExpression()))
|
||||
: builder.notEqual(upperIfIgnoreCase(path), upperIfIgnoreCase(expression.getExpression()));
|
||||
}
|
||||
case IS_EMPTY:
|
||||
case IS_NOT_EMPTY:
|
||||
|
||||
|
||||
@@ -208,7 +208,10 @@ class ParameterMetadataProvider {
|
||||
EscapeCharacter escape) {
|
||||
|
||||
this.expression = expression;
|
||||
this.type = value == null && Type.SIMPLE_PROPERTY.equals(part.getType()) ? Type.IS_NULL : part.getType();
|
||||
this.type = value == null
|
||||
&& (Type.SIMPLE_PROPERTY.equals(part.getType()) || Type.NEGATING_SIMPLE_PROPERTY.equals(part.getType()))
|
||||
? Type.IS_NULL
|
||||
: part.getType();
|
||||
this.ignoreCase = IgnoreCaseType.ALWAYS.equals(part.shouldIgnoreCase());
|
||||
this.noWildcards = part.getProperty().getLeafProperty().isCollection();
|
||||
this.escape = escape;
|
||||
|
||||
@@ -58,6 +58,7 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
* @author Krzysztof Krason
|
||||
* @author Greg Turnquist
|
||||
* @author Mark Paluch
|
||||
* @author Christoph Strobl
|
||||
* @see QueryLookupStrategy
|
||||
*/
|
||||
@ExtendWith(SpringExtension.class)
|
||||
@@ -386,4 +387,22 @@ class UserRepositoryFinderTests {
|
||||
assertThat(dtos).flatExtracting(UserRepository.NameOnly::getLastname) //
|
||||
.containsExactly("Matthews", "Beauford", "Matthews");
|
||||
}
|
||||
|
||||
@Test // GH-3675
|
||||
void findBySimplePropertyUsingMixedNullNonNullArgument() {
|
||||
|
||||
List<User> result = userRepository.findUserByLastname(null);
|
||||
assertThat(result).isEmpty();
|
||||
result = userRepository.findUserByLastname(carter.getLastname());
|
||||
assertThat(result).containsExactly(carter);
|
||||
}
|
||||
|
||||
@Test // GH-3675
|
||||
void findByNegatingSimplePropertyUsingMixedNullNonNullArgument() {
|
||||
|
||||
List<User> result = userRepository.findByLastnameNot(null);
|
||||
assertThat(result).isNotEmpty();
|
||||
result = userRepository.findUserByLastname(carter.getLastname());
|
||||
assertThat(result).containsExactly(carter);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,6 +37,8 @@ import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
@@ -59,6 +61,7 @@ import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
* @author Michael Cramer
|
||||
* @author Jens Schauder
|
||||
* @author Krzysztof Krason
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
@ExtendWith(SpringExtension.class)
|
||||
@ContextConfiguration("classpath:infrastructure.xml")
|
||||
@@ -100,20 +103,21 @@ class PartTreeJpaQueryIntegrationTests {
|
||||
testIgnoreCase("findByIdAllIgnoringCase", 3);
|
||||
}
|
||||
|
||||
@Test // DATAJPA-121
|
||||
@Disabled // HHH-15432
|
||||
void recreatesQueryIfNullValueIsGiven() throws Exception {
|
||||
@ParameterizedTest // DATAJPA-121, GH-3675
|
||||
@ValueSource(strings = { "Firstname", "FirstnameNot" })
|
||||
void recreatesQueryIfNullValueIsGiven(String criteria) throws Exception {
|
||||
|
||||
JpaQueryMethod queryMethod = getQueryMethod("findByFirstname", String.class, Pageable.class);
|
||||
JpaQueryMethod queryMethod = getQueryMethod("findBy%s".formatted(criteria), String.class, Pageable.class);
|
||||
PartTreeJpaQuery jpaQuery = new PartTreeJpaQuery(queryMethod, entityManager);
|
||||
|
||||
Query query = jpaQuery.createQuery(getAccessor(queryMethod, new Object[] { "Matthews", PageRequest.of(0, 1) }));
|
||||
|
||||
assertThat(HibernateUtils.getHibernateQuery(query.unwrap(HIBERNATE_NATIVE_QUERY))).endsWith("firstname=:param0");
|
||||
assertThat(HibernateUtils.getHibernateQuery(query.unwrap(HIBERNATE_NATIVE_QUERY)))
|
||||
.contains("firstname %s :".formatted(criteria.endsWith("Not") ? "<>" : "="));
|
||||
|
||||
query = jpaQuery.createQuery(getAccessor(queryMethod, new Object[] { null, PageRequest.of(0, 1) }));
|
||||
|
||||
assertThat(HibernateUtils.getHibernateQuery(query.unwrap(HIBERNATE_NATIVE_QUERY))).endsWith("firstname is null");
|
||||
assertThat(HibernateUtils.getHibernateQuery(query.unwrap(HIBERNATE_NATIVE_QUERY)))
|
||||
.endsWithIgnoringCase("firstname %s NULL".formatted(criteria.endsWith("Not") ? "IS NOT" : "IS"));
|
||||
}
|
||||
|
||||
@Test // DATAJPA-920
|
||||
@@ -277,6 +281,8 @@ class PartTreeJpaQueryIntegrationTests {
|
||||
|
||||
Page<User> findByFirstname(String firstname, Pageable pageable);
|
||||
|
||||
Page<User> findByFirstnameNot(String firstname, Pageable pageable);
|
||||
|
||||
User findByIdIgnoringCase(Integer id);
|
||||
|
||||
User findByIdAllIgnoringCase(Integer id);
|
||||
|
||||
@@ -48,6 +48,7 @@ import org.springframework.data.jpa.repository.query.Procedure;
|
||||
import org.springframework.data.querydsl.ListQuerydslPredicateExecutor;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
@@ -75,6 +76,8 @@ public interface UserRepository extends JpaRepository<User, Integer>, JpaSpecifi
|
||||
@QueryHints({ @QueryHint(name = "foo", value = "bar") })
|
||||
List<User> findByLastname(String lastname);
|
||||
|
||||
List<User> findUserByLastname(@Nullable String lastname);
|
||||
|
||||
/**
|
||||
* Redeclaration of {@link CrudRepository#findById(java.lang.Object)} to change transaction configuration.
|
||||
*/
|
||||
@@ -177,7 +180,7 @@ public interface UserRepository extends JpaRepository<User, Integer>, JpaSpecifi
|
||||
|
||||
List<User> findByLastnameNotLike(String lastname);
|
||||
|
||||
List<User> findByLastnameNot(String lastname);
|
||||
List<User> findByLastnameNot(@Nullable String lastname);
|
||||
|
||||
List<User> findByManagerLastname(String name);
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ The following table describes the keywords supported for JPA and what a method c
|
||||
|`EndingWith`|`findByFirstnameEndingWith`|`… where x.firstname like ?1` (parameter bound with prepended `%`)
|
||||
|`Containing`|`findByFirstnameContaining`|`… where x.firstname like ?1` (parameter bound wrapped in `%`)
|
||||
|`OrderBy`|`findByAgeOrderByLastnameDesc`|`… where x.age = ?1 order by x.lastname desc`
|
||||
|`Not`|`findByLastnameNot`|`… where x.lastname <> ?1`
|
||||
|`Not`|`findByLastnameNot`|`… where x.lastname <> ?1` (or `… where x.lastname IS NOT NULL` if the argument is `null`)
|
||||
|`In`|`findByAgeIn(Collection<Age> ages)`|`… where x.age in ?1`
|
||||
|`NotIn`|`findByAgeNotIn(Collection<Age> ages)`|`… where x.age not in ?1`
|
||||
|`True`|`findByActiveTrue()`|`… where x.active = true`
|
||||
|
||||
Reference in New Issue
Block a user