DATAJPA-863 - Polishing.

The error message now mentions the method including interface name. The Iterator in ParameterMetadataProvider now exposes which Part we tried to lookup a value for to report it in case no parameter value is available for anymore.

Switched to IllegalArgumentException to consistently produce the same exceptions. Removed wrapping of IllegalArgumentExceptions in CreateQueryLookupStrategy as PartTreeJpaQuery now produces IllegalArgumentExceptions in the first place.

Original pull request: #232.
This commit is contained in:
Oliver Gierke
2017-11-24 17:31:31 +01:00
parent f999800e5d
commit 589d6164eb
5 changed files with 109 additions and 18 deletions

View File

@@ -98,13 +98,7 @@ public final class JpaQueryLookupStrategy {
@Override
protected RepositoryQuery resolveQuery(JpaQueryMethod method, EntityManager em, NamedQueries namedQueries) {
try {
return new PartTreeJpaQuery(method, em, persistenceProvider);
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException(
String.format("Could not create query metamodel for method %s!", method.toString()), e);
}
return new PartTreeJpaQuery(method, em, persistenceProvider);
}
}

View File

@@ -119,6 +119,8 @@ class ParameterMetadataProvider {
@SuppressWarnings("unchecked")
public <T> ParameterMetadata<T> next(Part part) {
Assert.isTrue(parameters.hasNext(), String.format("No parameter available for part %s.", part));
Parameter parameter = parameters.next();
return (ParameterMetadata<T>) next(part, parameter.getType(), parameter);
}
@@ -159,7 +161,8 @@ class ParameterMetadataProvider {
Class<T> reifiedType = Expression.class.equals(type) ? (Class<T>) Object.class : type;
ParameterExpression<T> expression = parameter.isExplicitlyNamed()
? builder.parameter(reifiedType, parameter.getName()) : builder.parameter(reifiedType);
? builder.parameter(reifiedType, parameter.getName())
: builder.parameter(reifiedType);
ParameterMetadata<T> value = new ParameterMetadata<T>(expression, part.getType(),
bindableParameterValues == null ? ParameterMetadata.PLACEHOLDER : bindableParameterValues.next(),
this.persistenceProvider);
@@ -242,7 +245,8 @@ class ParameterMetadataProvider {
}
return Collection.class.isAssignableFrom(expressionType)
? persistenceProvider.potentiallyConvertEmptyCollection(toCollection(value)) : value;
? persistenceProvider.potentiallyConvertEmptyCollection(toCollection(value))
: value;
}
/**

View File

@@ -61,13 +61,20 @@ public class PartTreeJpaQuery extends AbstractJpaQuery {
this.em = em;
this.domainClass = method.getEntityInformation().getJavaType();
this.tree = new PartTree(method.getName(), domainClass);
this.parameters = method.getParameters();
boolean recreationRequired = parameters.hasDynamicProjection() || parameters.potentiallySortsDynamically();
this.countQuery = new CountQueryPreparer(persistenceProvider, recreationRequired);
this.query = tree.isCountProjection() ? countQuery : new QueryPreparer(persistenceProvider, recreationRequired);
try {
this.tree = new PartTree(method.getName(), domainClass);
this.countQuery = new CountQueryPreparer(persistenceProvider, recreationRequired);
this.query = tree.isCountProjection() ? countQuery : new QueryPreparer(persistenceProvider, recreationRequired);
} catch (Exception o_O) {
throw new IllegalArgumentException(
String.format("Failed to create query method %s! %s", method, o_O.getMessage()), o_O);
}
}
/*
@@ -96,9 +103,9 @@ public class PartTreeJpaQuery extends AbstractJpaQuery {
@Override
protected JpaQueryExecution getExecution() {
if(this.tree.isDelete()) {
if (this.tree.isDelete()) {
return new DeleteExecution(em);
} else if(this.tree.isExistsProjection()) {
} else if (this.tree.isExistsProjection()) {
return new ExistsExecution();
}
@@ -176,7 +183,7 @@ public class PartTreeJpaQuery extends AbstractJpaQuery {
query.setMaxResults(tree.getMaxResults());
}
if(tree.isExistsProjection()) {
if (tree.isExistsProjection()) {
query.setMaxResults(1);
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright 2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.jpa.repository.query;
import static org.mockito.Mockito.*;
import javax.persistence.criteria.CriteriaBuilder;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.data.jpa.provider.PersistenceProvider;
import org.springframework.data.repository.query.Parameters;
import org.springframework.data.repository.query.parser.Part;
/**
* Unit tests for {@link ParameterMetadataProvider}.
*
* @author Jens Schauder
* @author Oliver Gierke
*/
public class ParameterMetadataProviderUnitTests {
public @Rule ExpectedException exception = ExpectedException.none();
@Test // DATAJPA-863
public void errorMessageMentionesParametersWhenParametersAreExhausted() {
PersistenceProvider persistenceProvider = mock(PersistenceProvider.class);
CriteriaBuilder builder = mock(CriteriaBuilder.class);
Parameters<?, ?> parameters = mock(Parameters.class, RETURNS_DEEP_STUBS);
ParameterMetadataProvider metadataProvider = new ParameterMetadataProvider(builder, parameters,
persistenceProvider);
exception.expect(IllegalArgumentException.class);
exception.expectMessage("parameter");
metadataProvider.next(mock(Part.class));
}
}

View File

@@ -88,7 +88,7 @@ public class PartTreeJpaQueryIntegrationTests {
@Test
public void cannotIgnoreCaseIfNotString() throws Exception {
thrown.expect(IllegalStateException.class);
thrown.expect(IllegalArgumentException.class);
thrown.expectMessage("Unable to ignore case of java.lang.Integer types, the property 'id' must reference a String");
testIgnoreCase("findByIdIgnoringCase", 3);
}
@@ -120,7 +120,7 @@ public class PartTreeJpaQueryIntegrationTests {
JpaQueryMethod queryMethod = getQueryMethod("existsByFirstname", String.class);
PartTreeJpaQuery jpaQuery = new PartTreeJpaQuery(queryMethod, entityManager, provider);
Query query = jpaQuery.createQuery(new Object[]{"Matthews"});
Query query = jpaQuery.createQuery(new Object[] { "Matthews" });
assertThat(query.getMaxResults(), is(1));
}
@@ -131,11 +131,37 @@ public class PartTreeJpaQueryIntegrationTests {
JpaQueryMethod queryMethod = getQueryMethod("existsByFirstname", String.class);
PartTreeJpaQuery jpaQuery = new PartTreeJpaQuery(queryMethod, entityManager, provider);
Query query = jpaQuery.createQuery(new Object[]{"Matthews"});
Query query = jpaQuery.createQuery(new Object[] { "Matthews" });
assertThat(HibernateUtils.getHibernateQuery(getValue(query, PROPERTY)), containsString(".id from User as"));
}
@Test // DATAJPA-863
public void errorsDueToMismatchOfParametersContainNameOfMethodAndInterface() throws Exception {
JpaQueryMethod method = getQueryMethod("findByFirstname");
thrown.expect(IllegalArgumentException.class);
thrown.expectMessage("UserRepository"); // the repository
thrown.expectMessage("findByFirstname"); // the method being analyzed
thrown.expectMessage(" firstname "); // the property we are looking for
new PartTreeJpaQuery(method, entityManager, provider);
}
@Test // DATAJPA-863
public void errorsDueToMissingPropertyContainNameOfMethodAndInterface() throws Exception {
JpaQueryMethod method = getQueryMethod("findByNoSuchProperty", String.class);
thrown.expect(IllegalArgumentException.class);
thrown.expectMessage("UserRepository"); // the repository
thrown.expectMessage("findByNoSuchProperty"); // the method being analyzed
thrown.expectMessage(" noSuchProperty "); // the property we are looking for
new PartTreeJpaQuery(method, entityManager, provider);
}
private void testIgnoreCase(String methodName, Object... values) throws Exception {
Class<?>[] parameterTypes = new Class[values.length];
@@ -192,5 +218,11 @@ public class PartTreeJpaQueryIntegrationTests {
boolean existsByFirstname(String firstname);
List<User> findByCreatedAtAfter(@Temporal(TemporalType.TIMESTAMP) @Param("refDate") Date refDate);
// Wrong number of parameters
User findByFirstname();
// Wrong property name
User findByNoSuchProperty(String x);
}
}