Skip to content

Commit 8ef7992

Browse files
committed
Throw QueryCreationException if QueryExecutorMethodInterceptor cannot resolve a RepositoryQuery.
Move RepositoryException to repository.core to avoid package cycles. Extend QueryCreationException.
1 parent 13e5e39 commit 8ef7992

File tree

8 files changed

+114
-13
lines changed

8 files changed

+114
-13
lines changed

src/main/java/org/springframework/data/repository/core/support/RepositoryException.java renamed to src/main/java/org/springframework/data/repository/core/RepositoryException.java

+15-3
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,12 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16-
package org.springframework.data.repository.core.support;
16+
package org.springframework.data.repository.core;
1717

1818
import org.springframework.dao.InvalidDataAccessApiUsageException;
1919

2020
/**
21-
* Exception thrown in the context of repository creation/invocation.
21+
* Exception thrown in the context of repository creation or method invocation.
2222
*
2323
* @author Mark Paluch
2424
* @since 2.5
@@ -29,7 +29,7 @@ public class RepositoryException extends InvalidDataAccessApiUsageException {
2929
private final Class<?> repositoryInterface;
3030

3131
/**
32-
* Constructor for RepositoryCreationException.
32+
* Constructor for RepositoryException.
3333
*
3434
* @param msg the detail message.
3535
* @param repositoryInterface the repository interface.
@@ -39,6 +39,18 @@ public RepositoryException(String msg, Class<?> repositoryInterface) {
3939
this.repositoryInterface = repositoryInterface;
4040
}
4141

42+
/**
43+
* Constructor for RepositoryException.
44+
*
45+
* @param msg the detail message.
46+
* @param cause the root cause from the data access API in use.
47+
* @param repositoryInterface the repository interface.
48+
*/
49+
public RepositoryException(String msg, Throwable cause, Class<?> repositoryInterface) {
50+
super(msg, cause);
51+
this.repositoryInterface = repositoryInterface;
52+
}
53+
4254
public Class<?> getRepositoryInterface() {
4355
return repositoryInterface;
4456
}

src/main/java/org/springframework/data/repository/core/support/FragmentNotImplementedException.java

+2
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
*/
1616
package org.springframework.data.repository.core.support;
1717

18+
import org.springframework.data.repository.core.RepositoryException;
19+
1820
/**
1921
* Exception thrown during repository creation or repository method invocation when invoking a repository method on a
2022
* fragment without an implementation.

src/main/java/org/springframework/data/repository/core/support/IncompleteRepositoryCompositionException.java

+2
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
*/
1616
package org.springframework.data.repository.core.support;
1717

18+
import org.springframework.data.repository.core.RepositoryException;
19+
1820
/**
1921
* Exception thrown during repository creation or repository method invocation when a the repository has custom methods
2022
* that are not backed by a fragment or if no fragment could be found for a repository method invocation.

src/main/java/org/springframework/data/repository/core/support/QueryExecutorMethodInterceptor.java

+8-1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import org.springframework.data.repository.core.RepositoryInformation;
3030
import org.springframework.data.repository.core.support.RepositoryInvocationMulticaster.DefaultRepositoryInvocationMulticaster;
3131
import org.springframework.data.repository.core.support.RepositoryInvocationMulticaster.NoOpRepositoryInvocationMulticaster;
32+
import org.springframework.data.repository.query.QueryCreationException;
3233
import org.springframework.data.repository.query.QueryLookupStrategy;
3334
import org.springframework.data.repository.query.QueryMethod;
3435
import org.springframework.data.repository.query.RepositoryQuery;
@@ -97,7 +98,13 @@ private Map<Method, RepositoryQuery> mapMethodsToQuery(RepositoryInformation rep
9798

9899
private Pair<Method, RepositoryQuery> lookupQuery(Method method, RepositoryInformation information,
99100
QueryLookupStrategy strategy, ProjectionFactory projectionFactory) {
100-
return Pair.of(method, strategy.resolveQuery(method, information, projectionFactory, namedQueries));
101+
try {
102+
return Pair.of(method, strategy.resolveQuery(method, information, projectionFactory, namedQueries));
103+
} catch (QueryCreationException e) {
104+
throw e;
105+
} catch (RuntimeException e) {
106+
throw QueryCreationException.create(e.getMessage(), e, information.getRepositoryInterface(), method);
107+
}
101108
}
102109

103110
@SuppressWarnings({ "rawtypes", "unchecked" })

src/main/java/org/springframework/data/repository/core/support/UnsupportedFragmentException.java

+2
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
*/
1616
package org.springframework.data.repository.core.support;
1717

18+
import org.springframework.data.repository.core.RepositoryException;
19+
1820
/**
1921
* Exception thrown during repository creation when a well-known fragment interface is not supported by the repository
2022
* factory.

src/main/java/org/springframework/data/repository/query/QueryCreationException.java

+47-9
Original file line numberDiff line numberDiff line change
@@ -15,24 +15,39 @@
1515
*/
1616
package org.springframework.data.repository.query;
1717

18+
import java.lang.reflect.Method;
19+
20+
import org.springframework.data.repository.core.RepositoryException;
21+
1822
/**
19-
* Exception to be thrown if a query cannot be created from a {@link QueryMethod}.
23+
* Exception to be thrown if a query cannot be created from a {@link Method}.
2024
*
2125
* @author Oliver Gierke
26+
* @author Mark Paluch
2227
*/
23-
public final class QueryCreationException extends RuntimeException {
28+
public final class QueryCreationException extends RepositoryException {
2429

2530
private static final long serialVersionUID = -1238456123580L;
2631
private static final String MESSAGE_TEMPLATE = "Could not create query for method %s! Could not find property %s on domain class %s.";
2732

33+
private final Method method;
34+
35+
/**
36+
* Creates a new {@link QueryCreationException}.
37+
*/
38+
private QueryCreationException(String message, QueryMethod method) {
39+
40+
super(message, method.getMetadata().getRepositoryInterface());
41+
this.method = method.getMethod();
42+
}
43+
2844
/**
2945
* Creates a new {@link QueryCreationException}.
30-
*
31-
* @param method
3246
*/
33-
private QueryCreationException(String message) {
47+
private QueryCreationException(String message, Throwable cause, Class<?> repositoryInterface, Method method) {
3448

35-
super(message);
49+
super(message, cause, repositoryInterface);
50+
this.method = method;
3651
}
3752

3853
/**
@@ -45,7 +60,7 @@ private QueryCreationException(String message) {
4560
public static QueryCreationException invalidProperty(QueryMethod method, String propertyName) {
4661

4762
return new QueryCreationException(String.format(MESSAGE_TEMPLATE, method, propertyName, method.getDomainClass()
48-
.getName()));
63+
.getName()), method);
4964
}
5065

5166
/**
@@ -57,7 +72,8 @@ public static QueryCreationException invalidProperty(QueryMethod method, String
5772
*/
5873
public static QueryCreationException create(QueryMethod method, String message) {
5974

60-
return new QueryCreationException(String.format("Could not create query for %s! Reason: %s", method, message));
75+
return new QueryCreationException(String.format("Could not create query for %s! Reason: %s", method, message),
76+
method);
6177
}
6278

6379
/**
@@ -68,7 +84,29 @@ public static QueryCreationException create(QueryMethod method, String message)
6884
* @return
6985
*/
7086
public static QueryCreationException create(QueryMethod method, Throwable cause) {
87+
return new QueryCreationException(cause.getMessage(), cause, method.getMetadata().getRepositoryInterface(),
88+
method.getMethod());
89+
}
7190

72-
return create(method, cause.getMessage());
91+
/**
92+
* Creates a new {@link QueryCreationException} for the given {@link QueryMethod} and {@link Throwable} as cause.
93+
*
94+
* @param method
95+
* @param cause
96+
* @return
97+
* @since 2.5
98+
*/
99+
public static QueryCreationException create(String message, Throwable cause, Class<?> repositoryInterface,
100+
Method method) {
101+
return new QueryCreationException(String.format("Could not create query for %s! Reason: %s", method, message),
102+
cause, repositoryInterface, method);
103+
}
104+
105+
/**
106+
* @return
107+
* @since 2.5
108+
*/
109+
public Method getMethod() {
110+
return method;
73111
}
74112
}

src/main/java/org/springframework/data/repository/query/QueryMethod.java

+8
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,14 @@ public ResultProcessor getResultProcessor() {
241241
return resultProcessor;
242242
}
243243

244+
RepositoryMetadata getMetadata() {
245+
return metadata;
246+
}
247+
248+
Method getMethod() {
249+
return method;
250+
}
251+
244252
/*
245253
* (non-Javadoc)
246254
* @see java.lang.Object#toString()

src/test/java/org/springframework/data/repository/core/support/RepositoryFactorySupportUnitTests.java

+30
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,12 @@
6363
import org.springframework.data.repository.core.support.RepositoryMethodInvocationListener.RepositoryMethodInvocation;
6464
import org.springframework.data.repository.core.support.RepositoryMethodInvocationListener.RepositoryMethodInvocationResult.State;
6565
import org.springframework.data.repository.query.QueryByExampleExecutor;
66+
import org.springframework.data.repository.query.QueryCreationException;
67+
import org.springframework.data.repository.query.QueryLookupStrategy;
68+
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
6669
import org.springframework.data.repository.query.ReactiveQueryByExampleExecutor;
6770
import org.springframework.data.repository.query.RepositoryQuery;
71+
import org.springframework.data.repository.query.parser.PartTree;
6872
import org.springframework.data.repository.sample.User;
6973
import org.springframework.lang.Nullable;
7074
import org.springframework.scheduling.annotation.Async;
@@ -449,6 +453,25 @@ void dummyRepositoryNotSupportingReactiveQbeShouldRaiseException() {
449453
.hasMessageContaining("does not support Reactive Query by Example");
450454
}
451455

456+
@Test // GH-2341
457+
void derivedQueryMethodCannotBeImplemented() {
458+
459+
DummyRepositoryFactory factory = new DummyRepositoryFactory(backingRepo) {
460+
@Override
461+
protected Optional<QueryLookupStrategy> getQueryLookupStrategy(QueryLookupStrategy.Key key,
462+
QueryMethodEvaluationContextProvider evaluationContextProvider) {
463+
return Optional.of((method, metadata, factory, namedQueries) -> {
464+
new PartTree(method.getName(), method.getReturnType());
465+
return null;
466+
});
467+
}
468+
};
469+
470+
assertThatThrownBy(() -> factory.getRepository(WithQueryMethodUsingInvalidProperty.class))
471+
.isInstanceOf(QueryCreationException.class).hasMessageContaining("findAllByName")
472+
.hasMessageContaining("No property name found for type Object");
473+
}
474+
452475
private ConvertingRepository prepareConvertingRepository(final Object expectedValue) {
453476

454477
when(factory.queryOne.execute(any(Object[].class))).then(invocation -> {
@@ -586,4 +609,11 @@ interface WithQbe extends Repository<Object, Long>, QueryByExampleExecutor<Objec
586609
interface WithReactiveQbe extends Repository<Object, Long>, ReactiveQueryByExampleExecutor<Object> {
587610

588611
}
612+
613+
interface WithQueryMethodUsingInvalidProperty extends Repository<Object, Long> {
614+
615+
Object findAllByName();
616+
617+
}
618+
589619
}

0 commit comments

Comments
 (0)