diff --git a/pom.xml b/pom.xml index fe4332932..df0e13c95 100644 --- a/pom.xml +++ b/pom.xml @@ -55,7 +55,7 @@ 3.0.6.RELEASE 4.0.0.RELEASE [${spring.version.30}, ${spring.version.40}) - 1.2.0.BUILD-SNAPSHOT + 1.3.0.BUILD-SNAPSHOT 3.6.9.Final 2.1.1 2.3.2 diff --git a/src/main/java/org/springframework/data/jpa/repository/query/CriteriaQueryParameterBinder.java b/src/main/java/org/springframework/data/jpa/repository/query/CriteriaQueryParameterBinder.java index 576cdcaeb..9f61500fb 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/CriteriaQueryParameterBinder.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/CriteriaQueryParameterBinder.java @@ -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> expressions; + private final Iterator> 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> expressions) { + CriteriaQueryParameterBinder(Parameters parameters, Object[] values, Iterable> 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 expression = (ParameterExpression) expressions.next(); + ParameterMetadata parameterMetadata = (ParameterMetadata) expressions.next(); + + if (parameterMetadata.isIsNullParameter()) { + return; + } + + ParameterExpression expression = parameterMetadata.getExpression(); Object valueToBind = Collection.class.equals(expression.getJavaType()) ? toCollection(value) : value; - query.setParameter(expression, valueToBind); } diff --git a/src/main/java/org/springframework/data/jpa/repository/query/JpaCountQueryCreator.java b/src/main/java/org/springframework/data/jpa/repository/query/JpaCountQueryCreator.java index 0721ce92b..4ec54023d 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/JpaCountQueryCreator.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/JpaCountQueryCreator.java @@ -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); } /* diff --git a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java index 76a085965..6457be678 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java @@ -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, private final CriteriaBuilder builder; private final Root root; private final CriteriaQuery query; - private final ParameterExpressionProvider provider; + private final ParameterMetadataProvider provider; /** * Create a new {@link JpaQueryCreator}. @@ -62,14 +57,15 @@ public class JpaQueryCreator extends AbstractQueryCreator, * @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, * * @return the parameterExpressions */ - public List> getParameterExpressions() { - + public List> 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 iterator) { @@ -98,11 +89,7 @@ public class JpaQueryCreator extends AbstractQueryCreator, /* * (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 iterator) { @@ -112,10 +99,7 @@ public class JpaQueryCreator extends AbstractQueryCreator, /* * (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, */ protected CriteriaQuery complete(Predicate predicate, Sort sort, CriteriaQuery 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, * @return */ private Predicate toPredicate(Part part, Root root) { - return new PredicateBuilder(part, root).build(); } @@ -180,89 +162,6 @@ public class JpaQueryCreator extends AbstractQueryCreator, 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 parameters; - private final List> 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>(); - } - - /** - * Returns all {@link ParameterExpression}s built. - * - * @return the expressions - */ - public List> getExpressions() { - - return Collections.unmodifiableList(expressions); - } - - /** - * Builds a new {@link ParameterExpression} for the next {@link Parameter}. - * - * @param - * @return - */ - @SuppressWarnings("unchecked") - public ParameterExpression next() { - - Parameter parameter = parameters.next(); - return (ParameterExpression) next(parameter.getType(), parameter.getName()); - } - - /** - * Builds a new {@link ParameterExpression} of the given type. Forwards the underlying {@link Parameters} as well. - * - * @param - * @param type must not be {@literal null}. - * @return - */ - @SuppressWarnings("unchecked") - public ParameterExpression next(Class type) { - - Parameter parameter = parameters.next(); - Class typeToUse = ClassUtils.isAssignable(type, parameter.getType()) ? parameter.getType() : type; - return (ParameterExpression) next(typeToUse, null); - } - - /** - * Builds a new {@link ParameterExpression} for the given type and name. - * - * @param - * @param type must not be {@literal null}. - * @param name - * @return - */ - private ParameterExpression next(Class type, String name) { - - Assert.notNull(type); - - ParameterExpression 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, switch (part.getType()) { case BETWEEN: - ParameterExpression first = provider.next(); - ParameterExpression second = provider.next(); - return builder.between(getComparablePath(root, part), first, second); + ParameterMetadata first = provider.next(part); + ParameterMetadata 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 propertyExpression = upperIfIgnoreCase(getTypedPath(root, part, String.class)); - Expression parameterExpression = upperIfIgnoreCase(provider.next(String.class)); + Expression 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, case FALSE: return builder.isFalse(getTypedPath(root, part, Boolean.class)); case SIMPLE_PROPERTY: - return builder.equal(upperIfIgnoreCase(path), upperIfIgnoreCase(provider.next())); + ParameterMetadata 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()); } diff --git a/src/main/java/org/springframework/data/jpa/repository/query/ParameterMetadataProvider.java b/src/main/java/org/springframework/data/jpa/repository/query/ParameterMetadataProvider.java new file mode 100644 index 000000000..f56ab5935 --- /dev/null +++ b/src/main/java/org/springframework/data/jpa/repository/query/ParameterMetadataProvider.java @@ -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 parameters; + private final List> expressions; + private Iterator 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>(); + this.accessor = null; + } + + /** + * Returns all {@link ParameterMetadata}s built. + * + * @return the expressions + */ + public List> getExpressions() { + return Collections.unmodifiableList(expressions); + } + + /** + * Builds a new {@link ParameterMetadata} for given {@link Part} and the next {@link Parameter}. + * + * @param + * @return + */ + @SuppressWarnings("unchecked") + public ParameterMetadata next(Part part) { + + Parameter parameter = parameters.next(); + return (ParameterMetadata) 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 + * @param type must not be {@literal null}. + * @return + */ + @SuppressWarnings("unchecked") + public ParameterMetadata next(Part part, Class type) { + + Parameter parameter = parameters.next(); + Class typeToUse = ClassUtils.isAssignable(type, parameter.getType()) ? parameter.getType() : type; + return (ParameterMetadata) next(part, typeToUse, null); + } + + /** + * Builds a new {@link ParameterMetadata} for the given type and name. + * + * @param + * @param part must not be {@literal null}. + * @param type must not be {@literal null}. + * @param name + * @return + */ + private ParameterMetadata next(Part part, Class type, String name) { + + Assert.notNull(type); + + ParameterExpression expression = name == null ? builder.parameter(type) : builder.parameter(type, name); + ParameterMetadata value = new ParameterMetadata(expression, part.getType(), + accessor == null ? ParameterMetadata.PLACEHOLDER : accessor.next()); + expressions.add(value); + + return value; + } + + static class ParameterMetadata { + + static final Object PLACEHOLDER = new Object(); + + private final ParameterExpression expression; + private final Type type; + + public ParameterMetadata(ParameterExpression 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 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); + } + } +} \ No newline at end of file diff --git a/src/main/java/org/springframework/data/jpa/repository/query/PartTreeJpaQuery.java b/src/main/java/org/springframework/data/jpa/repository/query/PartTreeJpaQuery.java index bbcea7ab4..868c06bfe 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/PartTreeJpaQuery.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/PartTreeJpaQuery.java @@ -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> expressions; + private final List> 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> expressions = this.expressions; + List> 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> expressions) { - + private ParameterBinder getBinder(Object[] values, List> 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); } /** diff --git a/src/test/java/org/springframework/data/jpa/repository/query/ParameterExpressionProviderTests.java b/src/test/java/org/springframework/data/jpa/repository/query/ParameterExpressionProviderTests.java index 937a2f745..8ade09d0a 100644 --- a/src/test/java/org/springframework/data/jpa/repository/query/ParameterExpressionProviderTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/query/ParameterExpressionProviderTests.java @@ -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 expression = provider.next(Comparable.class); + ParameterMetadataProvider provider = new ParameterMetadataProvider(em.getCriteriaBuilder(), accessor); + ParameterExpression expression = provider.next(part, Comparable.class).getExpression(); assertThat(expression.getParameterType(), is(typeCompatibleWith(int.class))); } diff --git a/src/test/java/org/springframework/data/jpa/repository/query/PartTreeJpaQueryIntegrationTests.java b/src/test/java/org/springframework/data/jpa/repository/query/PartTreeJpaQueryIntegrationTests.java index 35b554f75..d1c78858e 100644 --- a/src/test/java/org/springframework/data/jpa/repository/query/PartTreeJpaQueryIntegrationTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/query/PartTreeJpaQueryIntegrationTests.java @@ -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 getValue(Object source, String path) { + + Iterator split = Arrays.asList(path.split("\\.")).iterator(); + Object result = source; + + while (split.hasNext()) { + result = getField(result, split.next()); + } + + return (T) result; + } + interface UserRepository extends Repository { Page findByFirstname(String firstname, Pageable pageable);