DATACMNS-102 - Allow Repositories to be composed of an arbitrary number of implementation classes.
We now use RepositoryComposition as backing implementation for repository method calls to implementations. We now scan for repository fragments during the repository configuration phase. Fragment implementation candidates derive from the repository interface declaration, specifically the declared interfaces and their order. We scan the class path during fragment scan for each interface and add discovered fragments to the repository composition. The name of the implementation is derived from the simple name of the interface and the implementation suffix. Qualified fragments are top-level interface declarations that are not annotated with NoRepositoryBean. Inherited interfaces are not considered as fragment candidates. We create a RepositoryComposition from the discovered fragments in the order of interface declaration in the repository interface and supply the composition to the actual repository creation. Original pull request: #222.
This commit is contained in:
committed by
Oliver Gierke
parent
5e123ad7df
commit
92e8907030
@@ -38,6 +38,7 @@ import org.springframework.data.util.Streamable;
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Thomas Darimont
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class AnnotationRepositoryConfigurationSourceUnitTests {
|
||||
|
||||
@@ -62,14 +63,13 @@ public class AnnotationRepositoryConfigurationSourceUnitTests {
|
||||
.contains(AnnotationRepositoryConfigurationSourceUnitTests.class.getPackage().getName());
|
||||
}
|
||||
|
||||
@Test // DATACMNS-47
|
||||
@Test // DATACMNS-47, DATACMNS-102
|
||||
public void evaluatesExcludeFiltersCorrectly() {
|
||||
|
||||
Streamable<BeanDefinition> candidates = source.getCandidates(new DefaultResourceLoader());
|
||||
assertThat(candidates).hasSize(1);
|
||||
|
||||
BeanDefinition candidate = candidates.iterator().next();
|
||||
assertThat(candidate.getBeanClassName()).isEqualTo(MyRepository.class.getName());
|
||||
assertThat(candidates).hasSize(2).extracting("beanClassName").containsOnly(MyRepository.class.getName(),
|
||||
ComposedRepository.class.getName());
|
||||
}
|
||||
|
||||
@Test // DATACMNS-47
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright 2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.repository.config;
|
||||
|
||||
import org.springframework.data.repository.Repository;
|
||||
import org.springframework.data.repository.config.AnnotationRepositoryConfigurationSourceUnitTests.Person;
|
||||
|
||||
/**
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public interface ComposedRepository extends Repository<Person, Long>, Mixin {}
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright 2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.repository.config;
|
||||
|
||||
/**
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public interface Mixin {
|
||||
|
||||
String getOne();
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.repository.config;
|
||||
|
||||
/**
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class MixinImpl implements Mixin {
|
||||
|
||||
@Override
|
||||
public String getOne() {
|
||||
return "one";
|
||||
}
|
||||
}
|
||||
@@ -29,9 +29,10 @@ import org.springframework.data.repository.config.RepositoryBeanDefinitionRegist
|
||||
|
||||
/**
|
||||
* Integration tests for {@link RepositoryBeanDefinitionRegistrarSupport}.
|
||||
*
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Peter Rietzler
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class RepositoryBeanDefinitionRegistrarSupportIntegrationTests {
|
||||
|
||||
@@ -85,4 +86,9 @@ public class RepositoryBeanDefinitionRegistrarSupportIntegrationTests {
|
||||
public void registersExtensionAsBeanDefinition() {
|
||||
assertThat(context.getBean(DummyConfigurationExtension.class)).isNotNull();
|
||||
}
|
||||
|
||||
@Test // DATACMNS-102
|
||||
public void composedRepositoriesShouldBeAssembledCorrectly() {
|
||||
assertThat(context.getBean(ComposedRepository.class).getOne()).isEqualTo("one");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,8 @@
|
||||
*/
|
||||
package org.springframework.data.repository.config;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.*;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
@@ -37,6 +38,7 @@ import org.springframework.data.repository.core.support.DummyRepositoryFactoryBe
|
||||
* Integration test for {@link RepositoryBeanDefinitionRegistrarSupport}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class RepositoryBeanDefinitionRegistrarSupportUnitTests {
|
||||
@@ -63,6 +65,9 @@ public class RepositoryBeanDefinitionRegistrarSupportUnitTests {
|
||||
registrar.registerBeanDefinitions(metadata, registry);
|
||||
|
||||
assertBeanDefinitionRegisteredFor("myRepository");
|
||||
assertBeanDefinitionRegisteredFor("composedRepository");
|
||||
assertBeanDefinitionRegisteredFor("mixinImpl");
|
||||
assertBeanDefinitionRegisteredFor("mixinImplFragment");
|
||||
assertNoBeanDefinitionRegisteredFor("profileRepository");
|
||||
}
|
||||
|
||||
@@ -100,7 +105,7 @@ public class RepositoryBeanDefinitionRegistrarSupportUnitTests {
|
||||
setResourceLoader(new DefaultResourceLoader());
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.config.RepositoryBeanDefinitionRegistrarSupport#getAnnotation()
|
||||
*/
|
||||
@@ -109,7 +114,7 @@ public class RepositoryBeanDefinitionRegistrarSupportUnitTests {
|
||||
return EnableRepositories.class;
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.config.RepositoryBeanDefinitionRegistrarSupport#getExtension()
|
||||
*/
|
||||
|
||||
@@ -38,6 +38,7 @@ import org.springframework.data.repository.core.RepositoryMetadata;
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Thomas Darimont
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class DefaultCrudMethodsUnitTests {
|
||||
|
||||
@@ -143,7 +144,7 @@ public class DefaultCrudMethodsUnitTests {
|
||||
|
||||
RepositoryMetadata metadata = new DefaultRepositoryMetadata(repositoryInterface);
|
||||
RepositoryInformation information = new DefaultRepositoryInformation(metadata, PagingAndSortingRepository.class,
|
||||
Optional.empty());
|
||||
RepositoryComposition.empty());
|
||||
|
||||
return new DefaultCrudMethods(information);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2016 the original author or authors.
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -64,7 +64,8 @@ public class DefaultRepositoryInformationUnitTests {
|
||||
|
||||
Method method = FooRepository.class.getMethod("findById", Integer.class);
|
||||
RepositoryMetadata metadata = new DefaultRepositoryMetadata(FooRepository.class);
|
||||
DefaultRepositoryInformation information = new DefaultRepositoryInformation(metadata, REPOSITORY, Optional.empty());
|
||||
DefaultRepositoryInformation information = new DefaultRepositoryInformation(metadata, REPOSITORY,
|
||||
RepositoryComposition.empty().withMethodLookup(MethodLookups.forRepositoryTypes(metadata)));
|
||||
|
||||
Method reference = information.getTargetClassMethod(method);
|
||||
assertThat(reference.getDeclaringClass()).isEqualTo(REPOSITORY);
|
||||
@@ -78,7 +79,7 @@ public class DefaultRepositoryInformationUnitTests {
|
||||
|
||||
RepositoryMetadata metadata = new DefaultRepositoryMetadata(FooRepository.class);
|
||||
DefaultRepositoryInformation information = new DefaultRepositoryInformation(metadata, CrudRepository.class,
|
||||
Optional.empty());
|
||||
RepositoryComposition.empty().withMethodLookup(MethodLookups.forRepositoryTypes(metadata)));
|
||||
|
||||
assertThat(information.getTargetClassMethod(method)).isEqualTo(method);
|
||||
}
|
||||
@@ -88,7 +89,7 @@ public class DefaultRepositoryInformationUnitTests {
|
||||
|
||||
RepositoryMetadata metadata = new DefaultRepositoryMetadata(FooRepository.class);
|
||||
RepositoryInformation information = new DefaultRepositoryInformation(metadata, CrudRepository.class,
|
||||
Optional.of(customImplementation.getClass()));
|
||||
RepositoryComposition.just(customImplementation));
|
||||
|
||||
Method source = FooRepositoryCustom.class.getMethod("save", User.class);
|
||||
Method expected = customImplementation.getClass().getMethod("save", User.class);
|
||||
@@ -101,7 +102,7 @@ public class DefaultRepositoryInformationUnitTests {
|
||||
|
||||
RepositoryMetadata metadata = new DefaultRepositoryMetadata(ConcreteRepository.class);
|
||||
RepositoryInformation information = new DefaultRepositoryInformation(metadata, CrudRepository.class,
|
||||
Optional.empty());
|
||||
RepositoryComposition.empty());
|
||||
|
||||
assertThat(information.hasCustomMethod()).isFalse();
|
||||
}
|
||||
@@ -111,7 +112,7 @@ public class DefaultRepositoryInformationUnitTests {
|
||||
|
||||
DefaultRepositoryMetadata metadata = new DefaultRepositoryMetadata(CustomRepository.class);
|
||||
DefaultRepositoryInformation information = new DefaultRepositoryInformation(metadata,
|
||||
PagingAndSortingRepository.class, Optional.empty());
|
||||
PagingAndSortingRepository.class, RepositoryComposition.empty());
|
||||
|
||||
Method method = CustomRepository.class.getMethod("findAll", Pageable.class);
|
||||
assertThat(information.isBaseClassMethod(method)).isTrue();
|
||||
@@ -127,7 +128,7 @@ public class DefaultRepositoryInformationUnitTests {
|
||||
|
||||
RepositoryMetadata metadata = new DefaultRepositoryMetadata(CustomRepository.class);
|
||||
RepositoryInformation information = new DefaultRepositoryInformation(metadata, PagingAndSortingRepository.class,
|
||||
Optional.empty());
|
||||
RepositoryComposition.empty());
|
||||
|
||||
assertThat(information.getQueryMethods()).isEmpty();
|
||||
}
|
||||
@@ -137,7 +138,7 @@ public class DefaultRepositoryInformationUnitTests {
|
||||
|
||||
RepositoryMetadata metadata = new DefaultRepositoryMetadata(ConcreteRepository.class);
|
||||
RepositoryInformation information = new DefaultRepositoryInformation(metadata, CrudRepository.class,
|
||||
Optional.empty());
|
||||
RepositoryComposition.empty());
|
||||
|
||||
Method saveMethod = BaseRepository.class.getMethod("save", Object.class);
|
||||
Method deleteMethod = BaseRepository.class.getMethod("delete", Object.class);
|
||||
@@ -152,7 +153,7 @@ public class DefaultRepositoryInformationUnitTests {
|
||||
|
||||
RepositoryMetadata metadata = new DefaultRepositoryMetadata(ConcreteRepository.class);
|
||||
RepositoryInformation information = new DefaultRepositoryInformation(metadata, CrudRepository.class,
|
||||
Optional.empty());
|
||||
RepositoryComposition.empty());
|
||||
|
||||
Method intermediateMethod = BaseRepository.class.getMethod("genericMethodToOverride", String.class);
|
||||
Method concreteMethod = ConcreteRepository.class.getMethod("genericMethodToOverride", String.class);
|
||||
@@ -168,7 +169,7 @@ public class DefaultRepositoryInformationUnitTests {
|
||||
|
||||
RepositoryMetadata metadata = new DefaultRepositoryMetadata(ConcreteRepository.class);
|
||||
RepositoryInformation information = new DefaultRepositoryInformation(metadata, CrudRepository.class,
|
||||
Optional.empty());
|
||||
RepositoryComposition.empty());
|
||||
|
||||
Method queryMethod = getMethodFrom(ConcreteRepository.class, "findBySomethingDifferent");
|
||||
|
||||
@@ -181,7 +182,7 @@ public class DefaultRepositoryInformationUnitTests {
|
||||
|
||||
RepositoryMetadata metadata = new DefaultRepositoryMetadata(ConcreteRepository.class);
|
||||
RepositoryInformation information = new DefaultRepositoryInformation(metadata, CrudRepository.class,
|
||||
Optional.empty());
|
||||
RepositoryComposition.empty());
|
||||
|
||||
Method method = BaseRepository.class.getMethod("findById", Object.class);
|
||||
|
||||
@@ -193,7 +194,7 @@ public class DefaultRepositoryInformationUnitTests {
|
||||
|
||||
RepositoryMetadata metadata = new DefaultRepositoryMetadata(BossRepository.class);
|
||||
RepositoryInformation information = new DefaultRepositoryInformation(metadata, CrudRepository.class,
|
||||
Optional.empty());
|
||||
RepositoryComposition.empty());
|
||||
|
||||
Method method = BossRepository.class.getMethod("saveAll", Iterable.class);
|
||||
Method reference = CrudRepository.class.getMethod("saveAll", Iterable.class);
|
||||
@@ -206,7 +207,7 @@ public class DefaultRepositoryInformationUnitTests {
|
||||
|
||||
RepositoryMetadata metadata = new DefaultRepositoryMetadata(CustomDefaultRepositoryMethodsRepository.class);
|
||||
RepositoryInformation information = new DefaultRepositoryInformation(metadata, CrudRepository.class,
|
||||
Optional.empty());
|
||||
RepositoryComposition.empty());
|
||||
|
||||
assertThat(information.getQueryMethods()).allMatch(method -> !method.isBridge());
|
||||
}
|
||||
@@ -216,7 +217,7 @@ public class DefaultRepositoryInformationUnitTests {
|
||||
|
||||
RepositoryMetadata metadata = new DefaultRepositoryMetadata(FooRepository.class);
|
||||
RepositoryInformation information = new DefaultRepositoryInformation(metadata, CrudRepository.class,
|
||||
Optional.of(customImplementation.getClass()));
|
||||
RepositoryComposition.just(customImplementation));
|
||||
|
||||
Method source = FooRepositoryCustom.class.getMethod("exists", Object.class);
|
||||
Method expected = customImplementation.getClass().getMethod("exists", Object.class);
|
||||
@@ -230,7 +231,7 @@ public class DefaultRepositoryInformationUnitTests {
|
||||
GenericsSaveRepositoryImpl customImplementation = new GenericsSaveRepositoryImpl();
|
||||
RepositoryMetadata metadata = new DefaultRepositoryMetadata(GenericsSaveRepository.class);
|
||||
RepositoryInformation information = new DefaultRepositoryInformation(metadata, RepositoryFactorySupport.class,
|
||||
Optional.of(customImplementation.getClass()));
|
||||
RepositoryComposition.just(customImplementation).withMethodLookup(MethodLookups.forRepositoryTypes(metadata)));
|
||||
|
||||
Method customBaseRepositoryMethod = GenericsSaveRepository.class.getMethod("save", Object.class);
|
||||
assertThat(information.isCustomMethod(customBaseRepositoryMethod)).isTrue();
|
||||
@@ -241,7 +242,7 @@ public class DefaultRepositoryInformationUnitTests {
|
||||
|
||||
RepositoryMetadata metadata = new DefaultRepositoryMetadata(FooRepository.class);
|
||||
RepositoryInformation information = new DefaultRepositoryInformation(metadata, CrudRepository.class,
|
||||
Optional.of(customImplementation.getClass()));
|
||||
RepositoryComposition.just(customImplementation));
|
||||
|
||||
Method method = FooRepository.class.getMethod("staticMethod");
|
||||
|
||||
@@ -253,7 +254,7 @@ public class DefaultRepositoryInformationUnitTests {
|
||||
|
||||
RepositoryMetadata metadata = new DefaultRepositoryMetadata(FooRepository.class);
|
||||
RepositoryInformation information = new DefaultRepositoryInformation(metadata, CrudRepository.class,
|
||||
Optional.of(customImplementation.getClass()));
|
||||
RepositoryComposition.just(customImplementation));
|
||||
|
||||
Method method = FooRepository.class.getMethod("defaultMethod");
|
||||
|
||||
@@ -265,7 +266,7 @@ public class DefaultRepositoryInformationUnitTests {
|
||||
|
||||
RepositoryMetadata metadata = new DefaultRepositoryMetadata(DummyRepository.class);
|
||||
RepositoryInformation information = new DefaultRepositoryInformation(metadata, DummyRepositoryImpl.class,
|
||||
Optional.empty());
|
||||
RepositoryComposition.empty());
|
||||
|
||||
Method method = DummyRepository.class.getMethod("saveAll", Iterable.class);
|
||||
|
||||
@@ -279,7 +280,7 @@ public class DefaultRepositoryInformationUnitTests {
|
||||
SimpleSaveRepositoryImpl customImplementation = new SimpleSaveRepositoryImpl();
|
||||
RepositoryMetadata metadata = new DefaultRepositoryMetadata(SimpleSaveRepository.class);
|
||||
RepositoryInformation information = new DefaultRepositoryInformation(metadata, RepositoryFactorySupport.class,
|
||||
Optional.of(customImplementation.getClass()));
|
||||
RepositoryComposition.just(customImplementation).withMethodLookup(MethodLookups.forRepositoryTypes(metadata)));
|
||||
|
||||
Method customBaseRepositoryMethod = SimpleSaveRepository.class.getMethod("save", Object.class);
|
||||
assertThat(information.isCustomMethod(customBaseRepositoryMethod)).isTrue();
|
||||
|
||||
@@ -24,14 +24,13 @@ import reactor.core.publisher.Flux;
|
||||
import rx.Observable;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import org.reactivestreams.Publisher;
|
||||
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.reactive.ReactiveCrudRepository;
|
||||
import org.springframework.data.repository.reactive.ReactiveSortingRepository;
|
||||
import org.springframework.data.repository.reactive.RxJava2CrudRepository;
|
||||
@@ -46,7 +45,7 @@ import org.springframework.data.repository.reactive.RxJava2CrudRepository;
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class ReactiveRepositoryInformationUnitTests {
|
||||
|
||||
static final Class<ReactiveJavaInterfaceWithGenerics> REPOSITORY = ReactiveJavaInterfaceWithGenerics.class;
|
||||
static final Class<ReactiveJavaInterfaceWithGenerics> BASE_CLASS = ReactiveJavaInterfaceWithGenerics.class;
|
||||
|
||||
@Test // DATACMNS-836
|
||||
public void discoversRxJava1MethodWithoutComparingReturnType() throws Exception {
|
||||
@@ -129,9 +128,12 @@ public class ReactiveRepositoryInformationUnitTests {
|
||||
private Method extractTargetMethodFromRepository(Class<?> repositoryType, String methodName, Class<?>... args)
|
||||
throws NoSuchMethodException {
|
||||
|
||||
RepositoryInformation information = new ReactiveRepositoryInformation(new DefaultRepositoryMetadata(repositoryType),
|
||||
REPOSITORY, Optional.empty());
|
||||
return information.getTargetClassMethod(repositoryType.getMethod(methodName, args));
|
||||
RepositoryMetadata metadata = new DefaultRepositoryMetadata(repositoryType);
|
||||
|
||||
RepositoryComposition composition = RepositoryComposition.of(RepositoryFragment.structural(BASE_CLASS))
|
||||
.withMethodLookup(MethodLookups.forReactiveTypes(metadata));
|
||||
|
||||
return composition.findMethod(repositoryType.getMethod(methodName, args)).get();
|
||||
}
|
||||
|
||||
interface RxJava1InterfaceWithGenerics extends Repository<User, String> {
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
package org.springframework.data.repository.core.support;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.*;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import io.reactivex.Completable;
|
||||
|
||||
@@ -0,0 +1,222 @@
|
||||
/*
|
||||
* Copyright 2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.repository.core.support;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.domain.Example;
|
||||
import org.springframework.data.repository.Repository;
|
||||
import org.springframework.data.repository.core.RepositoryInformation;
|
||||
import org.springframework.data.repository.core.support.RepositoryComposition.RepositoryFragments;
|
||||
import org.springframework.data.repository.query.QueryByExampleExecutor;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link RepositoryComposition}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class RepositoryCompositionUnitTests {
|
||||
|
||||
@Mock QueryByExampleExecutor<Person> queryByExampleExecutor;
|
||||
|
||||
@Mock PersonRepository backingRepo;
|
||||
|
||||
RepositoryComposition repositoryComposition;
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
|
||||
RepositoryInformation repositoryInformation = new DefaultRepositoryInformation(
|
||||
new DefaultRepositoryMetadata(PersonRepository.class), backingRepo.getClass(), RepositoryComposition.empty());
|
||||
|
||||
RepositoryFragment<QueryByExampleExecutor> mixin = RepositoryFragment.implemented(QueryByExampleExecutor.class,
|
||||
queryByExampleExecutor);
|
||||
|
||||
RepositoryFragment<PersonRepository> base = RepositoryFragment.implemented(backingRepo);
|
||||
|
||||
repositoryComposition = RepositoryComposition.of(RepositoryFragments.of(mixin, base))
|
||||
.withMethodLookup(MethodLookups.forRepositoryTypes(repositoryInformation));
|
||||
}
|
||||
|
||||
@Test // DATACMNS-102
|
||||
public void shouldReportIfEmpty() {
|
||||
|
||||
assertThat(RepositoryComposition.empty().isEmpty()).isTrue();
|
||||
assertThat(repositoryComposition.isEmpty()).isFalse();
|
||||
}
|
||||
|
||||
@Test // DATACMNS-102
|
||||
public void shouldCallSaveOnBackingRepo() throws Throwable {
|
||||
|
||||
Method save = ReflectionUtils.findMethod(PersonRepository.class, "save", Person.class);
|
||||
|
||||
Method method = repositoryComposition.findMethod(save).get();
|
||||
|
||||
Person person = new Person();
|
||||
repositoryComposition.invoke(method, person);
|
||||
|
||||
verify(backingRepo).save(person);
|
||||
}
|
||||
|
||||
@Test // DATACMNS-102
|
||||
public void shouldCallObjectSaveOnBackingRepo() throws Throwable {
|
||||
|
||||
Method save = ReflectionUtils.findMethod(PersonRepository.class, "save", Object.class);
|
||||
|
||||
Method method = repositoryComposition.findMethod(save).get();
|
||||
|
||||
Person person = new Person();
|
||||
repositoryComposition.invoke(method, person);
|
||||
|
||||
verify(backingRepo).save((Object) person);
|
||||
}
|
||||
|
||||
@Test // DATACMNS-102
|
||||
public void shouldCallFindOneOnMixin() throws Throwable {
|
||||
|
||||
Method findOne = ReflectionUtils.findMethod(PersonRepository.class, "findOne", Example.class);
|
||||
|
||||
Method method = repositoryComposition.findMethod(findOne).get();
|
||||
|
||||
Person person = new Person();
|
||||
Example<Person> example = Example.of(person);
|
||||
|
||||
repositoryComposition.invoke(method, example);
|
||||
|
||||
verify(queryByExampleExecutor).findOne(example);
|
||||
}
|
||||
|
||||
@Test // DATACMNS-102
|
||||
public void shouldCallMethodsInOrder() throws Throwable {
|
||||
|
||||
RepositoryInformation repositoryInformation = new DefaultRepositoryInformation(
|
||||
new DefaultRepositoryMetadata(OrderedRepository.class), OrderedRepository.class, RepositoryComposition.empty());
|
||||
|
||||
RepositoryFragment<?> foo = RepositoryFragment.implemented(FooMixinImpl.INSTANCE);
|
||||
RepositoryFragment<?> bar = RepositoryFragment.implemented(BarMixinImpl.INSTANCE);
|
||||
|
||||
RepositoryComposition fooBar = RepositoryComposition.of(RepositoryFragments.of(foo, bar))
|
||||
.withMethodLookup(MethodLookups.forRepositoryTypes(repositoryInformation));
|
||||
|
||||
RepositoryComposition barFoo = RepositoryComposition.of(RepositoryFragments.of(bar, foo))
|
||||
.withMethodLookup(MethodLookups.forRepositoryTypes(repositoryInformation));
|
||||
|
||||
Method getString = ReflectionUtils.findMethod(OrderedRepository.class, "getString");
|
||||
|
||||
assertThat(fooBar.invoke(fooBar.findMethod(getString).get())).isEqualTo("foo");
|
||||
|
||||
assertThat(barFoo.invoke(barFoo.findMethod(getString).get())).isEqualTo("bar");
|
||||
}
|
||||
|
||||
@Test // DATACMNS-102
|
||||
public void shouldValidateStructuralFragments() {
|
||||
|
||||
RepositoryComposition mixed = RepositoryComposition.of(RepositoryFragment.structural(QueryByExampleExecutor.class),
|
||||
RepositoryFragment.implemented(backingRepo));
|
||||
|
||||
assertThatExceptionOfType(IllegalStateException.class) //
|
||||
.isThrownBy(mixed::validateImplementation) //
|
||||
.withMessageContaining(
|
||||
"Fragment org.springframework.data.repository.query.QueryByExampleExecutor has no implementation.");
|
||||
}
|
||||
|
||||
@Test // DATACMNS-102
|
||||
public void shouldValidateImplementationFragments() {
|
||||
|
||||
RepositoryComposition mixed = RepositoryComposition.of(RepositoryFragment.implemented(backingRepo));
|
||||
|
||||
mixed.validateImplementation();
|
||||
}
|
||||
|
||||
@Test // DATACMNS-102
|
||||
@SuppressWarnings("rawtypes")
|
||||
public void shouldAppendCorrectly() {
|
||||
|
||||
RepositoryFragment<PersonRepository> initial = RepositoryFragment.implemented(backingRepo);
|
||||
RepositoryFragment<QueryByExampleExecutor> structural = RepositoryFragment.structural(QueryByExampleExecutor.class);
|
||||
|
||||
assertThat(RepositoryComposition.of(initial).append(structural).getFragments()).containsSequence(initial,
|
||||
structural);
|
||||
assertThat(RepositoryComposition.of(initial).append(RepositoryFragments.of(structural)).getFragments())
|
||||
.containsSequence(initial, structural);
|
||||
}
|
||||
|
||||
interface PersonRepository extends Repository<Person, String>, QueryByExampleExecutor<Person> {
|
||||
|
||||
Person save(Person entity);
|
||||
|
||||
Person save(Object entity);
|
||||
|
||||
Person findOne(Person entity);
|
||||
}
|
||||
|
||||
@Data
|
||||
static class Person {
|
||||
|
||||
@Id String id;
|
||||
}
|
||||
|
||||
@Data
|
||||
static class Contact {
|
||||
|
||||
@Id String id;
|
||||
}
|
||||
|
||||
interface OrderedRepository extends Repository<Person, String>, FooMixin, BarMixin {
|
||||
|
||||
}
|
||||
|
||||
interface FooMixin {
|
||||
|
||||
String getString();
|
||||
}
|
||||
|
||||
enum FooMixinImpl implements FooMixin {
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public String getString() {
|
||||
return "foo";
|
||||
}
|
||||
}
|
||||
|
||||
interface BarMixin {
|
||||
|
||||
String getString();
|
||||
}
|
||||
|
||||
enum BarMixinImpl implements BarMixin {
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public String getString() {
|
||||
return "bar";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2011-2016 the original author or authors.
|
||||
* Copyright 2011-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -51,6 +51,7 @@ import org.springframework.data.repository.core.EntityInformation;
|
||||
import org.springframework.data.repository.core.NamedQueries;
|
||||
import org.springframework.data.repository.core.RepositoryInformation;
|
||||
import org.springframework.data.repository.core.RepositoryMetadata;
|
||||
import org.springframework.data.repository.core.support.RepositoryComposition.RepositoryFragments;
|
||||
import org.springframework.data.repository.query.RepositoryQuery;
|
||||
import org.springframework.data.repository.sample.User;
|
||||
import org.springframework.data.util.Version;
|
||||
@@ -125,6 +126,17 @@ public class RepositoryFactorySupportUnitTests {
|
||||
verify(backingRepo, times(0)).findById(1);
|
||||
}
|
||||
|
||||
@Test // DATACMNS-102
|
||||
public void invokesCustomMethodCompositionMethodIfItRedeclaresACRUDOne() {
|
||||
|
||||
ObjectRepository repository = factory.getRepository(ObjectRepository.class,
|
||||
RepositoryFragments.just(customImplementation));
|
||||
repository.findById(1);
|
||||
|
||||
verify(customImplementation, times(1)).findById(1);
|
||||
verify(backingRepo, times(0)).findById(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createsRepositoryInstanceWithCustomIntermediateRepository() {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user