Skip to content

Commit a15ebdb

Browse files
committed
Polishing.
Refine error message format. Consistently use QueryCreationException. See #2736 Original pull request: #2738
1 parent cff0724 commit a15ebdb

File tree

8 files changed

+50
-51
lines changed

8 files changed

+50
-51
lines changed

spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractStringBasedJpaQuery.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import org.springframework.data.domain.Sort;
3030
import org.springframework.data.expression.ValueEvaluationContextProvider;
3131
import org.springframework.data.jpa.repository.QueryRewriter;
32+
import org.springframework.data.repository.query.QueryCreationException;
3233
import org.springframework.data.repository.query.ResultProcessor;
3334
import org.springframework.data.repository.query.ReturnedType;
3435
import org.springframework.data.repository.query.ValueExpressionDelegate;
@@ -124,8 +125,9 @@ public AbstractStringBasedJpaQuery(JpaQueryMethod method, EntityManager em, Decl
124125
}
125126
}
126127

127-
Assert.isTrue(method.isNativeQuery() || !this.query.usesJdbcStyleParameters(),
128-
"JDBC style parameters (?) are not supported for JPA queries");
128+
if (!method.isNativeQuery() && this.query.usesJdbcStyleParameters()) {
129+
throw QueryCreationException.create(method, "JDBC-style parameters (?) are not supported for JPA queries");
130+
}
129131
}
130132

131133
@Override

spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
import org.springframework.data.repository.core.RepositoryMetadata;
4444
import org.springframework.data.repository.query.Parameters;
4545
import org.springframework.data.repository.query.ParametersSource;
46+
import org.springframework.data.repository.query.QueryCreationException;
4647
import org.springframework.data.repository.query.QueryMethod;
4748
import org.springframework.data.repository.util.QueryExecutionConverters;
4849
import org.springframework.data.util.Lazy;
@@ -148,8 +149,10 @@ public JpaQueryMethod(Method method, RepositoryMetadata metadata, ProjectionFact
148149
this.metaAnnotation = Lazy
149150
.of(() -> Optional.ofNullable(AnnotatedElementUtils.findMergedAnnotation(method, Meta.class)));
150151

151-
Assert.isTrue(!(isModifyingQuery() && getParameters().hasSpecialParameter()),
152-
() -> String.format("Modifying method must not contain %s", Parameters.TYPES));
152+
if (isModifyingQuery() && getParameters().hasSpecialParameter()) {
153+
throw QueryCreationException.create(this,
154+
String.format("Modifying method must not contain %s", Parameters.TYPES));
155+
}
153156
}
154157

155158
private static Class<?> potentiallyUnwrapReturnTypeFor(RepositoryMetadata metadata, Method method) {

spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/NamedQuery.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,9 @@ private NamedQuery(JpaQueryMethod method, EntityManager em, JpaQueryConfiguratio
7575
Parameters<?, ?> parameters = method.getParameters();
7676

7777
if (parameters.hasSortParameter()) {
78-
throw new IllegalStateException(String.format("Query method %s is backed by a NamedQuery and must "
78+
throw QueryCreationException.create(method, String.format("Query method is backed by a NamedQuery and must "
7979
+ "not contain a sort parameter as we cannot modify the query; Use @%s(value=…) instead to apply sorting or remove the 'Sort' parameter.",
80-
method, method.isNativeQuery() ? "NativeQuery" : "Query"));
80+
method.isNativeQuery() ? "NativeQuery" : "Query"));
8181
}
8282

8383
this.namedCountQueryIsPresent = hasNamedQuery(em, countQueryName);

spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/PartTreeJpaQuery.java

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import org.springframework.data.jpa.repository.query.JpaQueryExecution.ScrollExecution;
3939
import org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformation;
4040
import org.springframework.data.jpa.repository.support.JpqlQueryTemplates;
41+
import org.springframework.data.repository.query.QueryCreationException;
4142
import org.springframework.data.repository.query.ResultProcessor;
4243
import org.springframework.data.repository.query.ReturnedType;
4344
import org.springframework.data.repository.query.parser.Part;
@@ -102,13 +103,12 @@ public class PartTreeJpaQuery extends AbstractJpaQuery {
102103
try {
103104

104105
this.tree = new PartTree(method.getName(), domainClass);
105-
validate(tree, parameters, method.toString());
106+
validate(tree, parameters);
106107
this.countQuery = new CountQueryPreparer();
107108
this.queryPreparer = tree.isCountProjection() ? countQuery : new QueryPreparer();
108109

109110
} catch (Exception o_O) {
110-
throw new IllegalArgumentException(
111-
String.format("Failed to create query for method %s; %s", method, o_O.getMessage()), o_O);
111+
throw QueryCreationException.create(getQueryMethod(), o_O);
112112
}
113113
}
114114

@@ -142,7 +142,7 @@ protected JpaQueryExecution getExecution(JpaParametersParameterAccessor accessor
142142
return super.getExecution(accessor);
143143
}
144144

145-
private static void validate(PartTree tree, JpaParameters parameters, String methodName) {
145+
private static void validate(PartTree tree, JpaParameters parameters) {
146146

147147
int argCount = 0;
148148

@@ -154,43 +154,43 @@ private static void validate(PartTree tree, JpaParameters parameters, String met
154154

155155
for (int i = 0; i < numberOfArguments; i++) {
156156

157-
throwExceptionOnArgumentMismatch(methodName, part, parameters, argCount);
157+
throwExceptionOnArgumentMismatch(part, parameters, argCount);
158158

159159
argCount++;
160160
}
161161
}
162162
}
163163

164-
private static void throwExceptionOnArgumentMismatch(String methodName, Part part, JpaParameters parameters,
164+
private static void throwExceptionOnArgumentMismatch(Part part, JpaParameters parameters,
165165
int index) {
166166

167167
Type type = part.getType();
168168
String property = part.getProperty().toDotPath();
169169

170170
if (!parameters.getBindableParameters().hasParameterAt(index)) {
171171
throw new IllegalStateException(String.format(
172-
"Method %s expects at least %d arguments but only found %d; This leaves an operator of type %s for property %s unbound",
173-
methodName, index + 1, index, type.name(), property));
172+
"Method expects at least %d arguments but only found %d; This leaves an operator of type '%s' for property '%s' unbound",
173+
index + 1, index, type.name(), property));
174174
}
175175

176176
JpaParameter parameter = parameters.getBindableParameter(index);
177177

178178
if (expectsCollection(type)) {
179179
if (!parameterIsCollectionLike(parameter)) {
180-
throw new IllegalStateException(wrongParameterTypeMessage(methodName, property, type, "Collection", parameter));
180+
throw new IllegalStateException(wrongParameterTypeMessage(property, type, "Collection", parameter));
181181
}
182182
} else {
183183
if (!part.getProperty().isCollection() && !parameterIsScalarLike(parameter)) {
184-
throw new IllegalStateException(wrongParameterTypeMessage(methodName, property, type, "scalar", parameter));
184+
throw new IllegalStateException(wrongParameterTypeMessage(property, type, "scalar", parameter));
185185
}
186186
}
187187
}
188188

189-
private static String wrongParameterTypeMessage(String methodName, String property, Type operatorType,
189+
private static String wrongParameterTypeMessage(String property, Type operatorType,
190190
String expectedArgumentType, JpaParameter parameter) {
191191

192-
return String.format("Operator %s on %s requires a %s argument, found %s in method %s", operatorType.name(),
193-
property, expectedArgumentType, parameter.getType(), methodName);
192+
return String.format("Operator '%s' on '%s' requires a %s argument, found '%s'", operatorType.name(), property,
193+
expectedArgumentType, parameter.getType());
194194
}
195195

196196
private static boolean parameterIsCollectionLike(JpaParameter parameter) {

spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/SimpleJpaQuery.java

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
import org.jspecify.annotations.Nullable;
2222

23+
import org.springframework.data.repository.query.QueryCreationException;
2324
import org.springframework.data.repository.query.RepositoryQuery;
2425

2526
/**
@@ -48,10 +49,10 @@ public SimpleJpaQuery(JpaQueryMethod method, EntityManager em, DeclaredQuery que
4849

4950
super(method, em, query, countQuery, queryConfiguration);
5051

51-
validateQuery(getQuery(), "Validation failed for query %s for method %s", method);
52+
validateQuery(getQuery(), "Query validation failed for '%s'", method);
5253

5354
if (method.isPageQuery()) {
54-
validateQuery(getCountQuery(), "Count query %s validation failed for method %s", method);
55+
validateQuery(getCountQuery(), "Count query validation failed for '%s'", method);
5556
}
5657
}
5758

@@ -67,18 +68,14 @@ private void validateQuery(QueryProvider query, String errorMessage, JpaQueryMet
6768
return;
6869
}
6970

70-
EntityManager validatingEm = null;
71-
var queryString = query.getQueryString();
72-
73-
try {
74-
validatingEm = getEntityManager().getEntityManagerFactory().createEntityManager();
71+
String queryString = query.getQueryString();
72+
try (EntityManager validatingEm = getEntityManager().getEntityManagerFactory().createEntityManager()) {
7573
validatingEm.createQuery(queryString);
76-
7774
} catch (RuntimeException e) {
7875

79-
// Needed as there's ambiguities in how an invalid query string shall be expressed by the persistence provider
80-
// https://download.oracle.com/javaee-archive/jpa-spec.java.net/users/2012/07/0404.html
81-
throw new IllegalArgumentException(errorMessage.formatted(query, method), e);
82-
}
76+
// Needed as there's ambiguities in how an invalid query string shall be expressed by the persistence provider
77+
// https://download.oracle.com/javaee-archive/jpa-spec.java.net/users/2012/07/0404.html
78+
throw QueryCreationException.create(method, errorMessage.formatted(queryString), e);
79+
}
8380
}
8481
}

spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategyUnitTests.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
import org.springframework.data.repository.core.NamedQueries;
4646
import org.springframework.data.repository.core.RepositoryMetadata;
4747
import org.springframework.data.repository.core.support.DefaultRepositoryMetadata;
48+
import org.springframework.data.repository.query.QueryCreationException;
4849
import org.springframework.data.repository.query.QueryLookupStrategy;
4950
import org.springframework.data.repository.query.QueryLookupStrategy.Key;
5051
import org.springframework.data.repository.query.RepositoryQuery;
@@ -58,6 +59,7 @@
5859
* @author Jens Schauder
5960
* @author Réda Housni Alaoui
6061
* @author Greg Turnquist
62+
* @author Mark Paluch
6163
*/
6264
@ExtendWith(MockitoExtension.class)
6365
@MockitoSettings(strictness = Strictness.LENIENT)
@@ -160,7 +162,7 @@ void namedQueryWithSortShouldThrowIllegalStateException() throws NoSuchMethodExc
160162
Method method = UserRepository.class.getMethod("customNamedQuery", String.class, Sort.class);
161163
RepositoryMetadata metadata = new DefaultRepositoryMetadata(UserRepository.class);
162164

163-
assertThatIllegalStateException()
165+
assertThatExceptionOfType(QueryCreationException.class)
164166
.isThrownBy(() -> strategy.resolveQuery(method, metadata, projectionFactory, namedQueries))
165167
.withMessageContaining(
166168
"is backed by a NamedQuery and must not contain a sort parameter as we cannot modify the query; Use @Query(value=…) instead to apply sorting or remove the 'Sort' parameter.");

spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/PartTreeJpaQueryIntegrationTests.java

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
import org.springframework.data.repository.Repository;
5151
import org.springframework.data.repository.core.support.DefaultRepositoryMetadata;
5252
import org.springframework.data.repository.query.Param;
53+
import org.springframework.data.repository.query.QueryCreationException;
5354
import org.springframework.test.context.ContextConfiguration;
5455
import org.springframework.test.context.junit.jupiter.SpringExtension;
5556

@@ -192,8 +193,7 @@ void rejectsInPredicateWithNonIterableParameter() throws Exception {
192193

193194
assertThatExceptionOfType(RuntimeException.class) //
194195
.isThrownBy(() -> new PartTreeJpaQuery(method, entityManager)) //
195-
.withMessageContaining("findByIdIn") //
196-
.withMessageContaining(" IN ") //
196+
.withMessageContaining("'IN'") //
197197
.withMessageContaining("Collection") //
198198
.withMessageContaining("Integer");
199199
}
@@ -203,11 +203,10 @@ void rejectsOtherThanInPredicateWithIterableParameter() throws Exception {
203203

204204
JpaQueryMethod method = getQueryMethod("findById", Collection.class);
205205

206-
assertThatExceptionOfType(RuntimeException.class) //
206+
assertThatExceptionOfType(QueryCreationException.class) //
207207
.isThrownBy(() -> new PartTreeJpaQuery(method, entityManager)) //
208-
.withMessageContaining("findById") //
209-
.withMessageContaining(" SIMPLE_PROPERTY ") //
210-
.withMessageContaining(" scalar ") //
208+
.withMessageContaining("'SIMPLE_PROPERTY'") //
209+
.withMessageContaining("scalar ") //
211210
.withMessageContaining("Collection");
212211
}
213212

@@ -226,23 +225,19 @@ void errorsDueToMismatchOfParametersContainNameOfMethodInterfaceAndPropertyPath(
226225

227226
JpaQueryMethod method = getQueryMethod("findByFirstname");
228227

229-
assertThatExceptionOfType(IllegalArgumentException.class) //
228+
assertThatExceptionOfType(QueryCreationException.class) //
230229
.isThrownBy(() -> new PartTreeJpaQuery(method, entityManager)) //
231-
.withMessageContaining("findByFirstname") // the method being analyzed
232-
.withMessageContaining(" firstname ") // the property we are looking for
233-
.withMessageContaining("UserRepository"); // the repository
230+
.withMessageContaining("'firstname'"); // the property we are looking for
234231
}
235232

236233
@Test // DATAJPA-863
237234
void errorsDueToMissingPropertyContainNameOfMethodAndInterface() throws Exception {
238235

239236
JpaQueryMethod method = getQueryMethod("findByNoSuchProperty", String.class);
240237

241-
assertThatExceptionOfType(IllegalArgumentException.class) //
238+
assertThatExceptionOfType(QueryCreationException.class) //
242239
.isThrownBy(() -> new PartTreeJpaQuery(method, entityManager)) //
243-
.withMessageContaining("findByNoSuchProperty") // the method being analyzed
244-
.withMessageContaining("'noSuchProperty'") // the property we are looking for
245-
.withMessageContaining("UserRepository"); // the repository
240+
.withMessageContaining("'noSuchProperty'"); // the property we are looking for
246241
}
247242

248243
@Test // GH-3356

spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/SimpleJpaQueryUnitTests.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
import org.springframework.data.repository.core.RepositoryMetadata;
5858
import org.springframework.data.repository.core.support.AbstractRepositoryMetadata;
5959
import org.springframework.data.repository.query.Param;
60+
import org.springframework.data.repository.query.QueryCreationException;
6061
import org.springframework.data.repository.query.RepositoryQuery;
6162
import org.springframework.data.repository.query.ResultProcessor;
6263
import org.springframework.data.repository.query.ValueExpressionDelegate;
@@ -192,17 +193,16 @@ void doesNotValidateCountQueryIfNotPagingMethod() throws Exception {
192193
createJpaQuery(method);
193194
}
194195

195-
@Test // DATAJPA-352
196+
@Test // DATAJPA-352, GH-2736
196197
void validatesAndRejectsCountQueryIfPagingMethod() throws Exception {
197198

198199
Method method = SampleRepository.class.getMethod("pageByAnnotatedQuery", Pageable.class);
199200

200201
when(em.createQuery(Mockito.contains("count"))).thenThrow(IllegalArgumentException.class);
201202

202-
assertThatIllegalArgumentException() //
203+
assertThatExceptionOfType(QueryCreationException.class) //
203204
.isThrownBy(() -> createJpaQuery(method)) //
204-
.withMessageContaining("Count") //
205-
.withMessageContaining(method.getName());
205+
.withMessageContaining("User u");
206206
}
207207

208208
@Test
@@ -323,7 +323,7 @@ void jdbcStyleParametersOnlyAllowedInNativeQueries() throws Exception {
323323

324324
Method illegalMethod = SampleRepository.class.getMethod("illegalUseOfJdbcStyleParameters", String.class);
325325

326-
assertThatIllegalArgumentException().isThrownBy(() -> createJpaQuery(illegalMethod));
326+
assertThatExceptionOfType(QueryCreationException.class).isThrownBy(() -> createJpaQuery(illegalMethod));
327327
}
328328

329329
@Test // GH-3929

0 commit comments

Comments
 (0)