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:
Oliver Gierke
2011-07-13 20:27:12 +02:00
parent d6ab8e37b3
commit 954c74b788
4 changed files with 116 additions and 30 deletions

View File

@@ -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;
}
}

View File

@@ -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));
}

View File

@@ -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> {
}
}

View File

@@ -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> {
}
}