Skip to content

Commit bdc79d0

Browse files
committed
JpaQueryLookupStrategy shouldn't use exceptions for flow control.
By using exceptions for flow control, other critical exceptions are getting masked. The lack of a resolvable query should instead leverage some sort of null value object. Closes #2018.
1 parent 406af9b commit bdc79d0

File tree

3 files changed

+53
-13
lines changed

3 files changed

+53
-13
lines changed

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

+31-10
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import org.springframework.data.repository.core.RepositoryMetadata;
2929
import org.springframework.data.repository.query.QueryLookupStrategy;
3030
import org.springframework.data.repository.query.QueryLookupStrategy.Key;
31+
import org.springframework.data.repository.query.QueryMethod;
3132
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
3233
import org.springframework.data.repository.query.RepositoryQuery;
3334
import org.springframework.lang.Nullable;
@@ -47,6 +48,11 @@ public final class JpaQueryLookupStrategy {
4748

4849
private static final Log LOG = LogFactory.getLog(JpaQueryLookupStrategy.class);
4950

51+
/**
52+
* A null-value instance used to signal if no named or annotated query could be resolved.
53+
*/
54+
private static final RepositoryQuery NO_NAMED_OR_ANNOTATED_QUERY = new NoNamedOrAnnotatedQuery();
55+
5056
/**
5157
* Private constructor to prevent instantiation.
5258
*/
@@ -172,12 +178,9 @@ protected RepositoryQuery resolveQuery(JpaQueryMethod method, QueryRewriter quer
172178

173179
RepositoryQuery query = NamedQuery.lookupFrom(method, em);
174180

175-
if (null != query) {
176-
return query;
177-
}
178-
179-
throw new IllegalStateException(
180-
String.format("Did neither find a NamedQuery nor an annotated query for method %s!", method));
181+
return query != null //
182+
? query //
183+
: NO_NAMED_OR_ANNOTATED_QUERY;
181184
}
182185

183186
@Nullable
@@ -245,11 +248,13 @@ public CreateIfNotFoundQueryLookupStrategy(EntityManager em, JpaQueryMethodFacto
245248
protected RepositoryQuery resolveQuery(JpaQueryMethod method, QueryRewriter queryRewriter, EntityManager em,
246249
NamedQueries namedQueries) {
247250

248-
try {
249-
return lookupStrategy.resolveQuery(method, queryRewriter, em, namedQueries);
250-
} catch (IllegalStateException e) {
251-
return createStrategy.resolveQuery(method, queryRewriter, em, namedQueries);
251+
RepositoryQuery lookupQuery = lookupStrategy.resolveQuery(method, queryRewriter, em, namedQueries);
252+
253+
if (lookupQuery != NO_NAMED_OR_ANNOTATED_QUERY) {
254+
return lookupQuery;
252255
}
256+
257+
return createStrategy.resolveQuery(method, queryRewriter, em, namedQueries);
253258
}
254259
}
255260

@@ -284,4 +289,20 @@ public static QueryLookupStrategy create(EntityManager em, JpaQueryMethodFactory
284289
throw new IllegalArgumentException(String.format("Unsupported query lookup strategy %s!", key));
285290
}
286291
}
292+
293+
/**
294+
* A null value type that represents the lack of a defined query.
295+
*/
296+
private static class NoNamedOrAnnotatedQuery implements RepositoryQuery {
297+
298+
@Override
299+
public Object execute(Object[] parameters) {
300+
return null;
301+
}
302+
303+
@Override
304+
public QueryMethod getQueryMethod() {
305+
return null;
306+
}
307+
}
287308
}

spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/User.java

+5-3
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@
1515
*/
1616
package org.springframework.data.jpa.domain.sample;
1717

18+
import jakarta.persistence.*;
19+
1820
import java.util.Arrays;
1921
import java.util.Date;
2022
import java.util.HashSet;
2123
import java.util.Set;
2224

23-
import jakarta.persistence.*;
24-
2525
/**
2626
* Domain class representing a person emphasizing the use of {@code AbstractEntity}. No declaration of an id is
2727
* required. The id is typed by the parameterizable superclass.
@@ -32,6 +32,7 @@
3232
* @author Jens Schauder
3333
* @author Jeff Sheets
3434
* @author JyotirmoyVS
35+
* @author Greg Turnquist
3536
*/
3637
@Entity
3738
@NamedEntityGraphs({ @NamedEntityGraph(name = "User.overview", attributeNodes = { @NamedAttributeNode("roles") }),
@@ -91,7 +92,8 @@
9192
@Table(name = "SD_User")
9293
public class User {
9394

94-
@Id @GeneratedValue(strategy = GenerationType.AUTO) private Integer id;
95+
@Id
96+
@GeneratedValue(strategy = GenerationType.AUTO) private Integer id;
9597
private String firstname;
9698
private String lastname;
9799
private int age;

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

+17
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,21 @@ void prefersDeclaredQuery() throws Exception {
169169
assertThat(repositoryQuery).isInstanceOf(AbstractStringBasedJpaQuery.class);
170170
}
171171

172+
@Test // GH-2018
173+
void namedQueryWithSortShouldThrowIllegalStateException() throws NoSuchMethodException {
174+
175+
QueryLookupStrategy strategy = JpaQueryLookupStrategy.create(em, queryMethodFactory, Key.CREATE_IF_NOT_FOUND,
176+
EVALUATION_CONTEXT_PROVIDER, new BeanFactoryQueryRewriterProvider(beanFactory), EscapeCharacter.DEFAULT);
177+
178+
Method method = UserRepository.class.getMethod("customNamedQuery", String.class, Sort.class);
179+
RepositoryMetadata metadata = new DefaultRepositoryMetadata(UserRepository.class);
180+
181+
assertThatIllegalStateException()
182+
.isThrownBy(() -> strategy.resolveQuery(method, metadata, projectionFactory, namedQueries))
183+
.withMessageContaining(
184+
"is backed by a NamedQuery and must not contain a sort parameter as we cannot modify the query! Use @Query instead!");
185+
}
186+
172187
interface UserRepository extends Repository<User, Integer> {
173188

174189
@Query("something absurd")
@@ -185,5 +200,7 @@ interface UserRepository extends Repository<User, Integer> {
185200

186201
@Query(value = "something absurd", name = "my-query-name")
187202
User annotatedQueryWithQueryAndQueryName();
203+
204+
List<User> customNamedQuery(String firstname, Sort sort);
188205
}
189206
}

0 commit comments

Comments
 (0)