DATAJPA-121 - Corrected query handling if null parameters are provided.

If a query is executed and null values are provided we now recreate a query with an IS NULL predicate correctly.
This commit is contained in:
Oliver Gierke
2012-02-02 14:49:01 +01:00
parent 9ae8caa1b8
commit 5c95016717
8 changed files with 265 additions and 150 deletions

View File

@@ -55,7 +55,7 @@
<spring.version.30>3.0.6.RELEASE</spring.version.30>
<spring.version.40>4.0.0.RELEASE</spring.version.40>
<spring.version.range>[${spring.version.30}, ${spring.version.40})</spring.version.range>
<spring.data.commons.version>1.2.0.BUILD-SNAPSHOT</spring.data.commons.version>
<spring.data.commons.version>1.3.0.BUILD-SNAPSHOT</spring.data.commons.version>
<hibernate.version>3.6.9.Final</hibernate.version>
<openjpa.version>2.1.1</openjpa.version>
<eclipselink.version>2.3.2</eclipselink.version>

View File

@@ -24,6 +24,7 @@ import java.util.Iterator;
import javax.persistence.Query;
import javax.persistence.criteria.ParameterExpression;
import org.springframework.data.jpa.repository.query.ParameterMetadataProvider.ParameterMetadata;
import org.springframework.data.repository.query.Parameter;
import org.springframework.data.repository.query.Parameters;
import org.springframework.util.Assert;
@@ -36,7 +37,7 @@ import org.springframework.util.ObjectUtils;
*/
class CriteriaQueryParameterBinder extends ParameterBinder {
private final Iterator<ParameterExpression<?>> expressions;
private final Iterator<ParameterMetadata<?>> expressions;
/**
* Creates a new {@link CriteriaQueryParameterBinder} for the given {@link Parameters}, values and some
@@ -44,7 +45,7 @@ class CriteriaQueryParameterBinder extends ParameterBinder {
*
* @param parameters
*/
CriteriaQueryParameterBinder(Parameters parameters, Object[] values, Iterable<ParameterExpression<?>> expressions) {
CriteriaQueryParameterBinder(Parameters parameters, Object[] values, Iterable<ParameterMetadata<?>> expressions) {
super(parameters, values);
Assert.notNull(expressions);
@@ -63,10 +64,15 @@ class CriteriaQueryParameterBinder extends ParameterBinder {
@SuppressWarnings("unchecked")
protected void bind(Query query, Parameter parameter, Object value, int position) {
ParameterExpression<Object> expression = (ParameterExpression<Object>) expressions.next();
ParameterMetadata<Object> parameterMetadata = (ParameterMetadata<Object>) expressions.next();
if (parameterMetadata.isIsNullParameter()) {
return;
}
ParameterExpression<Object> expression = parameterMetadata.getExpression();
Object valueToBind = Collection.class.equals(expression.getJavaType()) ? toCollection(value) : value;
query.setParameter(expression, valueToBind);
}

View File

@@ -15,14 +15,12 @@
*/
package org.springframework.data.jpa.repository.query;
import javax.persistence.EntityManager;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import org.springframework.data.domain.Sort;
import org.springframework.data.repository.query.Parameters;
import org.springframework.data.repository.query.parser.PartTree;
/**
@@ -40,9 +38,10 @@ public class JpaCountQueryCreator extends JpaQueryCreator {
* @param parameters
* @param em
*/
public JpaCountQueryCreator(PartTree tree, Class<?> domainClass, Parameters parameters, EntityManager em) {
public JpaCountQueryCreator(PartTree tree, Class<?> domainClass, CriteriaBuilder builder,
ParameterMetadataProvider provider) {
super(tree, domainClass, parameters, em);
super(tree, domainClass, builder, provider);
}
/*

View File

@@ -17,13 +17,10 @@ package org.springframework.data.jpa.repository.query;
import static org.springframework.data.jpa.repository.query.QueryUtils.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Expression;
@@ -32,15 +29,13 @@ import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.repository.query.ParameterMetadataProvider.ParameterMetadata;
import org.springframework.data.mapping.PropertyPath;
import org.springframework.data.repository.query.Parameter;
import org.springframework.data.repository.query.Parameters;
import org.springframework.data.repository.query.parser.AbstractQueryCreator;
import org.springframework.data.repository.query.parser.Part;
import org.springframework.data.repository.query.parser.Part.Type;
import org.springframework.data.repository.query.parser.PartTree;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
/**
* Query creator to create a {@link CriteriaQuery} from a {@link PartTree}.
@@ -52,7 +47,7 @@ public class JpaQueryCreator extends AbstractQueryCreator<CriteriaQuery<Object>,
private final CriteriaBuilder builder;
private final Root<?> root;
private final CriteriaQuery<Object> query;
private final ParameterExpressionProvider provider;
private final ParameterMetadataProvider provider;
/**
* Create a new {@link JpaQueryCreator}.
@@ -62,14 +57,15 @@ public class JpaQueryCreator extends AbstractQueryCreator<CriteriaQuery<Object>,
* @param accessor
* @param em
*/
public JpaQueryCreator(PartTree tree, Class<?> domainClass, Parameters parameters, EntityManager em) {
public JpaQueryCreator(PartTree tree, Class<?> domainClass, CriteriaBuilder builder,
ParameterMetadataProvider provider) {
super(tree);
this.builder = em.getCriteriaBuilder();
this.builder = builder;
this.query = builder.createQuery().distinct(tree.isDistinct());
this.root = query.from(domainClass);
this.provider = new ParameterExpressionProvider(builder, parameters.getBindableParameters());
this.provider = provider;
}
/**
@@ -77,18 +73,13 @@ public class JpaQueryCreator extends AbstractQueryCreator<CriteriaQuery<Object>,
*
* @return the parameterExpressions
*/
public List<ParameterExpression<?>> getParameterExpressions() {
public List<ParameterMetadata<?>> getParameterExpressions() {
return provider.getExpressions();
}
/*
* (non-Javadoc)
*
* @see
* org.springframework.data.repository.query.parser.AbstractQueryCreator
* #create(org.springframework.data.repository.query.parser.Part,
* java.util.Iterator)
* @see org.springframework.data.repository.query.parser.AbstractQueryCreator#create(org.springframework.data.repository.query.parser.Part, java.util.Iterator)
*/
@Override
protected Predicate create(Part part, Iterator<Object> iterator) {
@@ -98,11 +89,7 @@ public class JpaQueryCreator extends AbstractQueryCreator<CriteriaQuery<Object>,
/*
* (non-Javadoc)
*
* @see
* org.springframework.data.repository.query.parser.AbstractQueryCreator
* #and(org.springframework.data.repository.query.parser.Part,
* java.lang.Object, java.util.Iterator)
* @see org.springframework.data.repository.query.parser.AbstractQueryCreator#and(org.springframework.data.repository.query.parser.Part, java.lang.Object, java.util.Iterator)
*/
@Override
protected Predicate and(Part part, Predicate base, Iterator<Object> iterator) {
@@ -112,10 +99,7 @@ public class JpaQueryCreator extends AbstractQueryCreator<CriteriaQuery<Object>,
/*
* (non-Javadoc)
*
* @see
* org.springframework.data.repository.query.parser.AbstractQueryCreator
* #or(java.lang.Object, java.lang.Object)
* @see org.springframework.data.repository.query.parser.AbstractQueryCreator#or(java.lang.Object, java.lang.Object)
*/
@Override
protected Predicate or(Predicate base, Predicate predicate) {
@@ -146,7 +130,6 @@ public class JpaQueryCreator extends AbstractQueryCreator<CriteriaQuery<Object>,
*/
protected CriteriaQuery<Object> complete(Predicate predicate, Sort sort, CriteriaQuery<Object> query,
CriteriaBuilder builder, Root<?> root) {
return this.query.select(root).where(predicate).orderBy(QueryUtils.toOrders(sort, root, builder));
}
@@ -159,7 +142,6 @@ public class JpaQueryCreator extends AbstractQueryCreator<CriteriaQuery<Object>,
* @return
*/
private Predicate toPredicate(Part part, Root<?> root) {
return new PredicateBuilder(part, root).build();
}
@@ -180,89 +162,6 @@ public class JpaQueryCreator extends AbstractQueryCreator<CriteriaQuery<Object>,
return toExpressionRecursively(root, part.getProperty());
}
/**
* Helper class to allow easy creation of {@link ParameterExpression}s.
*
* @author Oliver Gierke
*/
static class ParameterExpressionProvider {
private final CriteriaBuilder builder;
private final Iterator<Parameter> parameters;
private final List<ParameterExpression<?>> expressions;
/**
* Creates a new {@link ParameterExpressionProvider} from the given {@link CriteriaBuilder} and {@link Parameters}.
*
* @param builder
* @param parameters
*/
public ParameterExpressionProvider(CriteriaBuilder builder, Parameters parameters) {
Assert.notNull(builder);
Assert.notNull(parameters);
this.builder = builder;
this.parameters = parameters.iterator();
this.expressions = new ArrayList<ParameterExpression<?>>();
}
/**
* Returns all {@link ParameterExpression}s built.
*
* @return the expressions
*/
public List<ParameterExpression<?>> getExpressions() {
return Collections.unmodifiableList(expressions);
}
/**
* Builds a new {@link ParameterExpression} for the next {@link Parameter}.
*
* @param <T>
* @return
*/
@SuppressWarnings("unchecked")
public <T> ParameterExpression<T> next() {
Parameter parameter = parameters.next();
return (ParameterExpression<T>) next(parameter.getType(), parameter.getName());
}
/**
* Builds a new {@link ParameterExpression} of the given type. Forwards the underlying {@link Parameters} as well.
*
* @param <T>
* @param type must not be {@literal null}.
* @return
*/
@SuppressWarnings("unchecked")
public <T> ParameterExpression<? extends T> next(Class<T> type) {
Parameter parameter = parameters.next();
Class<?> typeToUse = ClassUtils.isAssignable(type, parameter.getType()) ? parameter.getType() : type;
return (ParameterExpression<? extends T>) next(typeToUse, null);
}
/**
* Builds a new {@link ParameterExpression} for the given type and name.
*
* @param <T>
* @param type must not be {@literal null}.
* @param name
* @return
*/
private <T> ParameterExpression<T> next(Class<T> type, String name) {
Assert.notNull(type);
ParameterExpression<T> expression = name == null ? builder.parameter(type) : builder.parameter(type, name);
expressions.add(expression);
return expression;
}
}
/**
* Simple builder to contain logic to create JPA {@link Predicate}s from {@link Part}s.
*
@@ -300,29 +199,32 @@ public class JpaQueryCreator extends AbstractQueryCreator<CriteriaQuery<Object>,
switch (part.getType()) {
case BETWEEN:
ParameterExpression<Comparable> first = provider.next();
ParameterExpression<Comparable> second = provider.next();
return builder.between(getComparablePath(root, part), first, second);
ParameterMetadata<Comparable> first = provider.next(part);
ParameterMetadata<Comparable> second = provider.next(part);
return builder.between(getComparablePath(root, part), first.getExpression(), second.getExpression());
case GREATER_THAN:
return builder.greaterThan(getComparablePath(root, part), provider.next(Comparable.class));
return builder
.greaterThan(getComparablePath(root, part), provider.next(part, Comparable.class).getExpression());
case GREATER_THAN_EQUAL:
return builder.greaterThanOrEqualTo(getComparablePath(root, part), provider.next(Comparable.class));
return builder.greaterThanOrEqualTo(getComparablePath(root, part), provider.next(part, Comparable.class)
.getExpression());
case LESS_THAN:
return builder.lessThan(getComparablePath(root, part), provider.next(Comparable.class));
return builder.lessThan(getComparablePath(root, part), provider.next(part, Comparable.class).getExpression());
case LESS_THAN_EQUAL:
return builder.lessThanOrEqualTo(getComparablePath(root, part), provider.next(Comparable.class));
return builder.lessThanOrEqualTo(getComparablePath(root, part), provider.next(part, Comparable.class)
.getExpression());
case IS_NULL:
return path.isNull();
case IS_NOT_NULL:
return path.isNotNull();
case NOT_IN:
return path.in(provider.next(Collection.class)).not();
return path.in(provider.next(part, Collection.class).getExpression()).not();
case IN:
return path.in(provider.next(Collection.class));
return path.in(provider.next(part, Collection.class).getExpression());
case LIKE:
case NOT_LIKE:
Expression<String> propertyExpression = upperIfIgnoreCase(getTypedPath(root, part, String.class));
Expression<String> parameterExpression = upperIfIgnoreCase(provider.next(String.class));
Expression<String> parameterExpression = upperIfIgnoreCase(provider.next(part, String.class).getExpression());
Predicate like = builder.like(propertyExpression, parameterExpression);
return part.getType() == Type.LIKE ? like : like.not();
case TRUE:
@@ -330,9 +232,11 @@ public class JpaQueryCreator extends AbstractQueryCreator<CriteriaQuery<Object>,
case FALSE:
return builder.isFalse(getTypedPath(root, part, Boolean.class));
case SIMPLE_PROPERTY:
return builder.equal(upperIfIgnoreCase(path), upperIfIgnoreCase(provider.next()));
ParameterMetadata<Object> expression = provider.next(part);
return expression.isIsNullParameter() ? path.isNull() : builder.equal(upperIfIgnoreCase(path),
upperIfIgnoreCase(expression.getExpression()));
case NEGATING_SIMPLE_PROPERTY:
return builder.notEqual(upperIfIgnoreCase(path), upperIfIgnoreCase(provider.next()));
return builder.notEqual(upperIfIgnoreCase(path), upperIfIgnoreCase(provider.next(part).getExpression()));
default:
throw new IllegalArgumentException("Unsupported keyword + " + part.getType());
}

View File

@@ -0,0 +1,145 @@
package org.springframework.data.jpa.repository.query;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.ParameterExpression;
import org.springframework.data.repository.query.Parameter;
import org.springframework.data.repository.query.Parameters;
import org.springframework.data.repository.query.ParametersParameterAccessor;
import org.springframework.data.repository.query.parser.Part;
import org.springframework.data.repository.query.parser.Part.Type;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
/**
* Helper class to allow easy creation of {@link ParameterMetadata}s.
*
* @author Oliver Gierke
*/
class ParameterMetadataProvider {
private final CriteriaBuilder builder;
private final Iterator<Parameter> parameters;
private final List<ParameterMetadata<?>> expressions;
private Iterator<Object> accessor;
/**
* Creates a new {@link ParameterMetadataProvider} from the given {@link CriteriaBuilder} and
* {@link ParametersParameterAccessor}.
*
* @param builder must not be {@literal null}.
* @param parameters must not be {@literal null}.
*/
public ParameterMetadataProvider(CriteriaBuilder builder, ParametersParameterAccessor accessor) {
this(builder, accessor.getParameters());
Assert.notNull(accessor);
this.accessor = accessor.iterator();
}
public ParameterMetadataProvider(CriteriaBuilder builder, Parameters parameters) {
Assert.notNull(builder);
this.builder = builder;
this.parameters = parameters.getBindableParameters().iterator();
this.expressions = new ArrayList<ParameterMetadata<?>>();
this.accessor = null;
}
/**
* Returns all {@link ParameterMetadata}s built.
*
* @return the expressions
*/
public List<ParameterMetadata<?>> getExpressions() {
return Collections.unmodifiableList(expressions);
}
/**
* Builds a new {@link ParameterMetadata} for given {@link Part} and the next {@link Parameter}.
*
* @param <T>
* @return
*/
@SuppressWarnings("unchecked")
public <T> ParameterMetadata<T> next(Part part) {
Parameter parameter = parameters.next();
return (ParameterMetadata<T>) next(part, parameter.getType(), parameter.getName());
}
/**
* Builds a new {@link ParameterMetadata} of the given {@link Part} and type. Forwards the underlying
* {@link Parameters} as well.
*
* @param <T>
* @param type must not be {@literal null}.
* @return
*/
@SuppressWarnings("unchecked")
public <T> ParameterMetadata<? extends T> next(Part part, Class<T> type) {
Parameter parameter = parameters.next();
Class<?> typeToUse = ClassUtils.isAssignable(type, parameter.getType()) ? parameter.getType() : type;
return (ParameterMetadata<? extends T>) next(part, typeToUse, null);
}
/**
* Builds a new {@link ParameterMetadata} for the given type and name.
*
* @param <T>
* @param part must not be {@literal null}.
* @param type must not be {@literal null}.
* @param name
* @return
*/
private <T> ParameterMetadata<T> next(Part part, Class<T> type, String name) {
Assert.notNull(type);
ParameterExpression<T> expression = name == null ? builder.parameter(type) : builder.parameter(type, name);
ParameterMetadata<T> value = new ParameterMetadata<T>(expression, part.getType(),
accessor == null ? ParameterMetadata.PLACEHOLDER : accessor.next());
expressions.add(value);
return value;
}
static class ParameterMetadata<T> {
static final Object PLACEHOLDER = new Object();
private final ParameterExpression<T> expression;
private final Type type;
public ParameterMetadata(ParameterExpression<T> expression, Type type, Object value) {
this.expression = expression;
this.type = value == null && Type.SIMPLE_PROPERTY.equals(type) ? Type.IS_NULL : type;
}
/**
* Returns the {@link ParameterExpression}.
*
* @return the expression
*/
public ParameterExpression<T> getExpression() {
return expression;
}
/**
* Returns whether the parameter shall be considered an {@literal IS NULL} parameter.
*
* @return
*/
public boolean isIsNullParameter() {
return Type.IS_NULL.equals(type);
}
}
}

View File

@@ -20,10 +20,11 @@ import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.ParameterExpression;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.repository.query.ParameterMetadataProvider.ParameterMetadata;
import org.springframework.data.repository.query.Parameters;
import org.springframework.data.repository.query.ParametersParameterAccessor;
import org.springframework.data.repository.query.parser.PartTree;
@@ -89,11 +90,11 @@ public class PartTreeJpaQuery extends AbstractJpaQuery {
private class QueryPreparer {
private final CriteriaQuery<?> query;
private final List<ParameterExpression<?>> expressions;
private final List<ParameterMetadata<?>> expressions;
public QueryPreparer(boolean recreateQueries) {
JpaQueryCreator creator = createCreator();
JpaQueryCreator creator = createCreator(null);
this.query = recreateQueries ? null : creator.createQuery();
this.expressions = recreateQueries ? null : creator.getParameterExpressions();
}
@@ -107,10 +108,11 @@ public class PartTreeJpaQuery extends AbstractJpaQuery {
public Query createQuery(Object[] values) {
CriteriaQuery<?> criteriaQuery = query;
List<ParameterExpression<?>> expressions = this.expressions;
List<ParameterMetadata<?>> expressions = this.expressions;
ParametersParameterAccessor accessor = new ParametersParameterAccessor(parameters, values);
if (query == null) {
JpaQueryCreator creator = createCreator();
if (query == null || accessor.hasBindableNullValue()) {
JpaQueryCreator creator = createCreator(accessor);
criteriaQuery = creator.createQuery(getDynamicSort(values));
expressions = creator.getParameterExpressions();
}
@@ -119,9 +121,15 @@ public class PartTreeJpaQuery extends AbstractJpaQuery {
return invokeBinding(getBinder(values, expressions), jpaQuery);
}
protected JpaQueryCreator createCreator() {
protected JpaQueryCreator createCreator(ParametersParameterAccessor accessor) {
return new JpaQueryCreator(tree, domainClass, parameters, getEntityManager());
EntityManager entityManager = getEntityManager();
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
ParameterMetadataProvider provider = accessor == null ? new ParameterMetadataProvider(builder, parameters)
: new ParameterMetadataProvider(builder, accessor);
return new JpaQueryCreator(tree, domainClass, builder, provider);
}
/**
@@ -136,8 +144,7 @@ public class PartTreeJpaQuery extends AbstractJpaQuery {
return binder.bindAndPrepare(query);
}
private ParameterBinder getBinder(Object[] values, List<ParameterExpression<?>> expressions) {
private ParameterBinder getBinder(Object[] values, List<ParameterMetadata<?>> expressions) {
return new CriteriaQueryParameterBinder(parameters, values, expressions);
}
@@ -167,9 +174,15 @@ public class PartTreeJpaQuery extends AbstractJpaQuery {
* QueryPreparer#createCreator()
*/
@Override
protected JpaQueryCreator createCreator() {
protected JpaQueryCreator createCreator(ParametersParameterAccessor accessor) {
return new JpaCountQueryCreator(tree, domainClass, parameters, getEntityManager());
EntityManager entityManager = getEntityManager();
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
ParameterMetadataProvider provider = accessor == null ? new ParameterMetadataProvider(builder, parameters)
: new ParameterMetadataProvider(builder, accessor);
return new JpaCountQueryCreator(tree, domainClass, builder, provider);
}
/**

View File

@@ -12,13 +12,14 @@ import javax.persistence.criteria.ParameterExpression;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.data.jpa.domain.sample.User;
import org.springframework.data.jpa.repository.query.JpaQueryCreator.ParameterExpressionProvider;
import org.springframework.data.repository.query.Parameters;
import org.springframework.data.repository.query.ParametersParameterAccessor;
import org.springframework.data.repository.query.parser.Part;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* Integration tests for {@link ParameterExpressionProvider}.
* Integration tests for {@link ParameterMetadataProvider}.
*
* @author Oliver Gierke
*/
@@ -38,9 +39,11 @@ public class ParameterExpressionProviderTests {
Method method = SampleRepository.class.getMethod("findByIdGreaterThan", int.class);
Parameters parameters = new Parameters(method);
ParametersParameterAccessor accessor = new ParametersParameterAccessor(parameters, new Object[] { 1 });
Part part = new Part("IdGreaterThan", User.class);
ParameterExpressionProvider provider = new ParameterExpressionProvider(em.getCriteriaBuilder(), parameters);
ParameterExpression<? extends Comparable> expression = provider.next(Comparable.class);
ParameterMetadataProvider provider = new ParameterMetadataProvider(em.getCriteriaBuilder(), accessor);
ParameterExpression<? extends Comparable> expression = provider.next(part, Comparable.class).getExpression();
assertThat(expression.getParameterType(), is(typeCompatibleWith(int.class)));
}

View File

@@ -1,7 +1,9 @@
/*
* Copyright 2011 the original author or authors.
* Copyright 2011-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* Licensed under the Apache License, Version 2.0 (the "License
import org.springframework.aop.framework.Advised;
");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
@@ -15,11 +17,19 @@
*/
package org.springframework.data.jpa.repository.query;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import static org.springframework.test.util.ReflectionTestUtils.*;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Iterator;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import org.hibernate.ejb.HibernateQuery;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
@@ -79,6 +89,28 @@ public class PartTreeJpaQueryIntegrationTests {
testIgnoreCase("findByIdAllIgnoringCase", 3);
}
/**
* @see DATAJPA-121
*/
@Test
public void recreatesQueryIfNullValueIsGiven() throws Exception {
Method method = UserRepository.class.getMethod("findByFirstname", String.class, Pageable.class);
JpaQueryMethod queryMethod = new JpaQueryMethod(method, new DefaultRepositoryMetadata(UserRepository.class),
PersistenceProvider.fromEntityManager(entityManager));
PartTreeJpaQuery jpaQuery = new PartTreeJpaQuery(queryMethod, entityManager);
Query query = jpaQuery.createQuery(new Object[] { "Matthews", new PageRequest(0, 1) });
HibernateQuery hibernateQuery = getValue(query, "h.target.val$jpaqlQuery");
assertThat(hibernateQuery.getHibernateQuery().getQueryString(), endsWith("firstname=:param0"));
query = jpaQuery.createQuery(new Object[] { null, new PageRequest(0, 1) });
hibernateQuery = getValue(query, "h.target.val$jpaqlQuery");
assertThat(hibernateQuery.getHibernateQuery().getQueryString(), endsWith("firstname is null"));
}
private void testIgnoreCase(String methodName, Object... values) throws Exception {
Class<?>[] parameterTypes = new Class[values.length];
@@ -92,6 +124,19 @@ public class PartTreeJpaQueryIntegrationTests {
jpaQuery.createQuery(values);
}
@SuppressWarnings("unchecked")
private static <T> T getValue(Object source, String path) {
Iterator<String> split = Arrays.asList(path.split("\\.")).iterator();
Object result = source;
while (split.hasNext()) {
result = getField(result, split.next());
}
return (T) result;
}
interface UserRepository extends Repository<User, Long> {
Page<User> findByFirstname(String firstname, Pageable pageable);