Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
* @author Christoph Strobl
* @author John Blum
* @author Johannes Englmeier
* @author Donghwan Kim
*/
class QueryExecutorMethodInterceptor implements MethodInterceptor {

Expand Down Expand Up @@ -137,7 +138,7 @@ private void invokeListeners(RepositoryQuery query) {
public @Nullable Object invoke(MethodInvocation invocation) throws Throwable {

Method method = invocation.getMethod();
MethodParameter returnType = returnTypeMap.computeIfAbsent(method, it -> new MethodParameter(it, -1));
MethodParameter returnType = returnTypeMap.computeIfAbsent(method, this::createReturnTypeParameter);

QueryExecutionConverters.ExecutionAdapter executionAdapter = QueryExecutionConverters //
.getExecutionAdapter(returnType.getParameterType());
Expand All @@ -150,6 +151,16 @@ private void invokeListeners(RepositoryQuery query) {
.apply(() -> resultHandler.postProcessInvocationResult(doInvoke(invocation), returnType));
}

/**
* Creates the {@link MethodParameter} describing the return type of the given {@link Method}. The repository
* interface is registered as containing class so that a return type declared as type variable on a base interface
* (e.g. {@code T findById(ID id)}) resolves against the concrete repository interface instead of the type variable's
* bound.
*/
private MethodParameter createReturnTypeParameter(Method method) {
return new MethodParameter(method, -1).withContainingClass(repositoryInformation.getRepositoryInterface());
}

@SuppressWarnings("NullAway")
private @Nullable Object doInvoke(MethodInvocation invocation) throws Throwable {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@
* @author Mark Paluch
* @author Ariel Carrera
* @author Johannes Englmeier
* @author Donghwan Kim
*/
@ExtendWith(MockitoExtension.class)
@MockitoSettings(strictness = Strictness.LENIENT)
Expand Down Expand Up @@ -458,6 +459,17 @@ void shouldAllowVoidMethods() {
verify(backingRepo).deleteAll();
}

@Test // GH-3507
void unwrapsOptionalForTypeVariableReturnTypeDeclaredOnBaseInterface() {

var user = new User();
when(backingRepo.findById(any())).thenReturn(Optional.of(user));

var repository = factory.getRepository(TypeVariableReturningRepository.class);

assertThat(repository.findById(1L)).isSameAs(user);
}

@Test // DATACMNS-1154
void considersRequiredKotlinParameter() {

Expand Down Expand Up @@ -635,6 +647,15 @@ interface CustomRepository extends ReadOnlyRepository<Object, Long> {

}

interface TypeVariableReturningBaseRepository<T, ID> extends Repository<T, ID> {

T findById(ID id);
}

interface TypeVariableReturningRepository extends TypeVariableReturningBaseRepository<User, Long> {

}

@RepositoryDefinition(domainClass = Object.class, idClass = Long.class)
interface AnnotatedRepository {

Expand Down
Loading