Improve error message if store module doesn't support a well-known fragment interface.

We now throw a RepositoryCreationException (or subclass) when a repository cannot be created due to a missing fragment, a fragment without implementation or if a well-known fragment is not supported by the repository factory.

Throw QueryCreationException if QueryExecutorMethodInterceptor cannot resolve a RepositoryQuery.

Closes #2341
Original pull request: #2342.
This commit is contained in:
Mark Paluch
2021-03-25 12:19:27 +01:00
parent 1abd534ab0
commit e47faf8aa2
11 changed files with 434 additions and 31 deletions

View File

@@ -136,16 +136,17 @@ class RepositoryCompositionUnitTests {
assertThat(barFoo.invoke(barFoo.findMethod(getString).get())).isEqualTo("bar");
}
@Test // DATACMNS-102
@Test // DATACMNS-102, GH-2341
void shouldValidateStructuralFragments() {
RepositoryComposition mixed = RepositoryComposition.of(RepositoryFragment.structural(QueryByExampleExecutor.class),
RepositoryFragment.implemented(backingRepo));
assertThatIllegalStateException() //
assertThatExceptionOfType(FragmentNotImplementedException.class) //
.isThrownBy(mixed::validateImplementation) //
.withMessageContaining(
"Fragment org.springframework.data.repository.query.QueryByExampleExecutor has no implementation.");
"Fragment org.springframework.data.repository.query.QueryByExampleExecutor")
.withMessageContaining("has no implementation");
}
@Test // DATACMNS-102

View File

@@ -51,6 +51,7 @@ import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.projection.ProjectionFactory;
import org.springframework.data.querydsl.QuerydslPredicateExecutor;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.repository.Repository;
import org.springframework.data.repository.RepositoryDefinition;
@@ -61,7 +62,13 @@ import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.data.repository.core.support.RepositoryComposition.RepositoryFragments;
import org.springframework.data.repository.core.support.RepositoryMethodInvocationListener.RepositoryMethodInvocation;
import org.springframework.data.repository.core.support.RepositoryMethodInvocationListener.RepositoryMethodInvocationResult.State;
import org.springframework.data.repository.query.QueryByExampleExecutor;
import org.springframework.data.repository.query.QueryCreationException;
import org.springframework.data.repository.query.QueryLookupStrategy;
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
import org.springframework.data.repository.query.ReactiveQueryByExampleExecutor;
import org.springframework.data.repository.query.RepositoryQuery;
import org.springframework.data.repository.query.parser.PartTree;
import org.springframework.data.repository.sample.User;
import org.springframework.lang.Nullable;
import org.springframework.scheduling.annotation.Async;
@@ -422,6 +429,49 @@ class RepositoryFactorySupportUnitTests {
orderedInvocation.verify(startup).start("spring.data.repository.proxy");
}
@Test // GH-2341
void dummyRepositoryShouldsupportQuerydsl() {
factory.getRepository(WithQuerydsl.class, backingRepo);
}
@Test // GH-2341
void dummyRepositoryNotSupportingReactiveQuerydslShouldRaiseException() {
assertThatThrownBy(() -> factory.getRepository(WithReactiveQuerydsl.class, backingRepo))
.isInstanceOf(UnsupportedFragmentException.class).hasMessage(
"Repository org.springframework.data.repository.core.support.RepositoryFactorySupportUnitTests$WithReactiveQuerydsl implements org.springframework.data.repository.query.ReactiveQueryByExampleExecutor but DummyRepositoryFactory does not support Reactive Query by Example!");
}
@Test // GH-2341
void dummyRepositoryNotSupportingQbeShouldRaiseException() {
assertThatThrownBy(() -> factory.getRepository(WithQbe.class, backingRepo))
.hasMessageContaining("does not support Query by Example");
}
@Test // GH-2341
void dummyRepositoryNotSupportingReactiveQbeShouldRaiseException() {
assertThatThrownBy(() -> factory.getRepository(WithReactiveQbe.class, backingRepo))
.hasMessageContaining("does not support Reactive Query by Example");
}
@Test // GH-2341
void derivedQueryMethodCannotBeImplemented() {
DummyRepositoryFactory factory = new DummyRepositoryFactory(backingRepo) {
@Override
protected Optional<QueryLookupStrategy> getQueryLookupStrategy(QueryLookupStrategy.Key key,
QueryMethodEvaluationContextProvider evaluationContextProvider) {
return Optional.of((method, metadata, factory, namedQueries) -> {
new PartTree(method.getName(), method.getReturnType());
return null;
});
}
};
assertThatThrownBy(() -> factory.getRepository(WithQueryMethodUsingInvalidProperty.class))
.isInstanceOf(QueryCreationException.class).hasMessageContaining("findAllByName")
.hasMessageContaining("No property name found for type Object");
}
private ConvertingRepository prepareConvertingRepository(final Object expectedValue) {
when(factory.queryOne.execute(any(Object[].class))).then(invocation -> {
@@ -543,4 +593,27 @@ class RepositoryFactorySupportUnitTests {
CustomRepositoryBaseClass(EntityInformation<?, ?> information) {}
}
interface WithQuerydsl extends Repository<Object, Long>, QuerydslPredicateExecutor<Object> {
}
interface WithReactiveQuerydsl extends Repository<Object, Long>, ReactiveQueryByExampleExecutor<Object> {
}
interface WithQbe extends Repository<Object, Long>, QueryByExampleExecutor<Object> {
}
interface WithReactiveQbe extends Repository<Object, Long>, ReactiveQueryByExampleExecutor<Object> {
}
interface WithQueryMethodUsingInvalidProperty extends Repository<Object, Long> {
Object findAllByName();
}
}