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:
2
pom.xml
2
pom.xml
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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)));
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user