DATACMNS-52 - Redeclared methods in intermediate repository interfaces are now discovered correctly.
We deeply have to resolve the generic types before doing the type comparison. Tweaked DefaultRepositoryInformation and added test cases.
This commit is contained in:
@@ -16,6 +16,7 @@
|
||||
package org.springframework.data.repository.core.support;
|
||||
|
||||
import static org.springframework.data.repository.util.ClassUtils.*;
|
||||
import static org.springframework.core.GenericTypeResolver.*;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Type;
|
||||
@@ -25,6 +26,8 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.springframework.core.GenericTypeResolver;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.data.repository.Repository;
|
||||
import org.springframework.data.repository.core.RepositoryInformation;
|
||||
import org.springframework.data.repository.core.RepositoryMetadata;
|
||||
@@ -248,23 +251,20 @@ class DefaultRepositoryInformation implements RepositoryInformation {
|
||||
|
||||
Type[] genericTypes = baseClassMethod.getGenericParameterTypes();
|
||||
Class<?>[] types = baseClassMethod.getParameterTypes();
|
||||
Class<?>[] methodParameters = method.getParameterTypes();
|
||||
|
||||
for (int i = 0; i < genericTypes.length; i++) {
|
||||
|
||||
Type type = genericTypes[i];
|
||||
MethodParameter parameter = new MethodParameter(method, i);
|
||||
Class<?> parameterType = resolveParameterType(parameter, metadata.getRepositoryInterface());
|
||||
|
||||
if (type instanceof TypeVariable<?>) {
|
||||
|
||||
String name = ((TypeVariable<?>) type).getName();
|
||||
|
||||
if (!matchesGenericType(name, methodParameters[i])) {
|
||||
if (!matchesGenericType(name, parameterType)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if (!types[i].equals(methodParameters[i])) {
|
||||
if (!types[i].equals(parameterType)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,9 +18,10 @@ package org.springframework.data.querydsl;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import com.mysema.query.annotations.QueryEntity;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.mysema.query.annotations.QueryEntity;
|
||||
|
||||
|
||||
/**
|
||||
* Unit test for {@link SimpleEntityPathResolver}.
|
||||
@@ -35,15 +36,15 @@ public class SimpleEntityPathResolverUnitTests {
|
||||
@Test
|
||||
public void createsRepositoryFromDomainClassCorrectly() throws Exception {
|
||||
|
||||
assertThat(resolver.createPath(User.class), is(QUser.class));
|
||||
assertThat((QUser) resolver.createPath(User.class), isA(QUser.class));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void resolvesEntityPathForInnerClassCorrectly() throws Exception {
|
||||
|
||||
assertThat(resolver.createPath(NamedUser.class),
|
||||
is(QSimpleEntityPathResolverUnitTests_NamedUser.class));
|
||||
assertThat((QSimpleEntityPathResolverUnitTests_NamedUser) resolver.createPath(NamedUser.class),
|
||||
isA(QSimpleEntityPathResolverUnitTests_NamedUser.class));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -5,17 +5,23 @@ import static org.junit.Assert.*;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
|
||||
import org.hamcrest.Matcher;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
import org.springframework.data.repository.PagingAndSortingRepository;
|
||||
import org.springframework.data.repository.Repository;
|
||||
import org.springframework.data.repository.core.RepositoryInformation;
|
||||
import org.springframework.data.repository.core.RepositoryMetadata;
|
||||
import org.springframework.data.repository.core.support.DefaultRepositoryInformation;
|
||||
import org.springframework.data.repository.core.support.DefaultRepositoryMetadata;
|
||||
import org.springframework.data.repository.core.support.DefaultRepositoryMetadataUnitTests.DummyGenericRepositorySupport;
|
||||
import org.springframework.data.repository.core.support.RepositoryFactorySupportUnitTests.ReadOnlyRepository;
|
||||
|
||||
/**
|
||||
* @author Oliver Gierke
|
||||
@@ -25,7 +31,7 @@ public class DefaultRepositoryInformationUnitTests {
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
static final Class<DummyGenericRepositorySupport> REPOSITORY = DummyGenericRepositorySupport.class;
|
||||
|
||||
|
||||
@Mock
|
||||
FooRepositoryCustom customImplementation;
|
||||
|
||||
@@ -51,28 +57,55 @@ public class DefaultRepositoryInformationUnitTests {
|
||||
|
||||
assertThat(information.getTargetClassMethod(method), is(method));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void discoversCustomlyImplementedCrudMethod() throws SecurityException, NoSuchMethodException {
|
||||
|
||||
RepositoryMetadata metadata = new DefaultRepositoryMetadata(FooRepository.class);
|
||||
RepositoryInformation information = new DefaultRepositoryInformation(metadata, CrudRepository.class, customImplementation.getClass());
|
||||
|
||||
RepositoryInformation information = new DefaultRepositoryInformation(metadata, CrudRepository.class,
|
||||
customImplementation.getClass());
|
||||
|
||||
Method source = FooRepositoryCustom.class.getMethod("save", User.class);
|
||||
Method expected = customImplementation.getClass().getMethod("save", User.class);
|
||||
|
||||
|
||||
assertThat(information.getTargetClassMethod(source), is(expected));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void considersIntermediateMethodsAsFinderMethods() {
|
||||
|
||||
|
||||
RepositoryMetadata metadata = new DefaultRepositoryMetadata(ConcreteRepository.class);
|
||||
RepositoryInformation information = new DefaultRepositoryInformation(metadata, CrudRepository.class, null);
|
||||
|
||||
|
||||
assertThat(information.hasCustomMethod(), is(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void discoversIntermediateMethodsAsBackingMethods() throws NoSuchMethodException, SecurityException {
|
||||
|
||||
DefaultRepositoryMetadata metadata = new DefaultRepositoryMetadata(CustomRepository.class);
|
||||
DefaultRepositoryInformation information = new DefaultRepositoryInformation(metadata,
|
||||
PagingAndSortingRepository.class, null);
|
||||
|
||||
Method method = CustomRepository.class.getMethod("findAll", Pageable.class);
|
||||
assertThat(information.isBaseClassMethod(method), is(true));
|
||||
|
||||
method = getMethodFrom(CustomRepository.class, "exists");
|
||||
assertThat(information.isBaseClassMethod(method), is(true));
|
||||
|
||||
Matcher<Iterable<Method>> empty = iterableWithSize(0);
|
||||
assertThat(information.getQueryMethods(), is(empty));
|
||||
}
|
||||
|
||||
private Method getMethodFrom(Class<?> type, String name) {
|
||||
for (Method method : type.getMethods()) {
|
||||
if (method.getName().equals(name)) {
|
||||
return method;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
interface FooRepository extends CrudRepository<User, Integer>, FooRepositoryCustom {
|
||||
|
||||
// Redeclared method
|
||||
@@ -81,9 +114,9 @@ public class DefaultRepositoryInformationUnitTests {
|
||||
// Not a redeclared method
|
||||
User findOne(Long primaryKey);
|
||||
}
|
||||
|
||||
|
||||
interface FooRepositoryCustom {
|
||||
|
||||
|
||||
User save(User user);
|
||||
}
|
||||
|
||||
@@ -98,14 +131,32 @@ public class DefaultRepositoryInformationUnitTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
interface BaseRepository<T, ID extends Serializable> extends CrudRepository<T, ID> {
|
||||
|
||||
|
||||
T findBySomething(String something);
|
||||
}
|
||||
|
||||
|
||||
interface ConcreteRepository extends BaseRepository<User, Integer> {
|
||||
|
||||
|
||||
User findBySomethingDifferent(String somethingDifferent);
|
||||
}
|
||||
|
||||
interface ReadOnlyRepository<T, ID extends Serializable> extends Repository<T, ID> {
|
||||
|
||||
T findOne(ID id);
|
||||
|
||||
Iterable<T> findAll();
|
||||
|
||||
Page<T> findAll(Pageable pageable);
|
||||
|
||||
List<T> findAll(Sort sort);
|
||||
|
||||
boolean exists(ID id);
|
||||
|
||||
long count();
|
||||
}
|
||||
|
||||
interface CustomRepository extends ReadOnlyRepository<Object, Long> {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,12 +20,17 @@ import static org.mockito.Mockito.*;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.repository.PagingAndSortingRepository;
|
||||
import org.springframework.data.repository.Repository;
|
||||
import org.springframework.data.repository.core.EntityInformation;
|
||||
import org.springframework.data.repository.core.NamedQueries;
|
||||
@@ -46,7 +51,7 @@ public class RepositoryFactorySupportUnitTests {
|
||||
RepositoryFactorySupport factory = new DummyRepositoryFactory();
|
||||
|
||||
@Mock
|
||||
CrudRepository<Object, Serializable> backingRepo;
|
||||
PagingAndSortingRepository<Object, Serializable> backingRepo;
|
||||
@Mock
|
||||
ObjectRepositoryCustom customImplementation;
|
||||
|
||||
@@ -87,6 +92,16 @@ public class RepositoryFactorySupportUnitTests {
|
||||
verify(customImplementation, times(1)).findOne(1);
|
||||
verify(backingRepo, times(0)).findOne(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createsRepositoryInstanceWithCustomIntermediateRepository() {
|
||||
|
||||
CustomRepository repository = factory.getRepository(CustomRepository.class);
|
||||
Pageable pageable = new PageRequest(0, 10);
|
||||
repository.findAll(pageable);
|
||||
|
||||
verify(backingRepo, times(1)).findAll(pageable);
|
||||
}
|
||||
|
||||
|
||||
class DummyRepositoryFactory extends RepositoryFactorySupport {
|
||||
@@ -112,7 +127,7 @@ public class RepositoryFactorySupportUnitTests {
|
||||
@Override
|
||||
protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {
|
||||
|
||||
return CrudRepository.class;
|
||||
return backingRepo.getClass();
|
||||
}
|
||||
|
||||
|
||||
@@ -157,4 +172,23 @@ public class RepositoryFactorySupportUnitTests {
|
||||
interface MyRepositoryQuery extends RepositoryQuery {
|
||||
|
||||
}
|
||||
|
||||
interface ReadOnlyRepository<T, ID extends Serializable> extends Repository<T, ID> {
|
||||
|
||||
T findOne(ID id);
|
||||
|
||||
Iterable<T> findAll();
|
||||
|
||||
Page<T> findAll(Pageable pageable);
|
||||
|
||||
List<T> findAll(Sort sort);
|
||||
|
||||
boolean exists(ID id);
|
||||
|
||||
long count();
|
||||
}
|
||||
|
||||
interface CustomRepository extends ReadOnlyRepository<Object, Long> {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user