Browse Source

DATACMNS-763 - Fixed constructor in example of custom repository base class.

Added hint to which constructor of the superclass to override. Added test cas e to make sure the expected types are advertised in the case of an error.
pull/151/head
Oliver Gierke 11 years ago
parent
commit
79b15df7e1
  1. 16
      src/main/asciidoc/repositories.adoc
  2. 10
      src/main/java/org/springframework/data/repository/core/support/RepositoryFactorySupport.java
  3. 27
      src/test/java/org/springframework/data/repository/core/support/RepositoryFactorySupportUnitTests.java

16
src/main/asciidoc/repositories.adoc

@ -107,6 +107,7 @@ Standard CRUD functionality repositories usually have queries on the underlying @@ -107,6 +107,7 @@ Standard CRUD functionality repositories usually have queries on the underlying
. Declare an interface extending Repository or one of its subinterfaces and type it to the domain class and ID type that it will handle.
+
[source, java]
----
interface PersonRepository extends Repository<Person, Long> { … }
@ -114,6 +115,7 @@ interface PersonRepository extends Repository<Person, Long> { … } @@ -114,6 +115,7 @@ interface PersonRepository extends Repository<Person, Long> { … }
. Declare query methods on the interface.
+
[source, java]
----
interface PersonRepository extends Repository<Person, Long> {
@ -123,6 +125,7 @@ interface PersonRepository extends Repository<Person, Long> { @@ -123,6 +125,7 @@ interface PersonRepository extends Repository<Person, Long> {
. Set up Spring to create proxy instances for those interfaces. Either via <<repositories.create-instances.java-config,JavaConfig>>:
+
[source, java]
----
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
@ -130,9 +133,11 @@ import org.springframework.data.jpa.repository.config.EnableJpaRepositories; @@ -130,9 +133,11 @@ import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
@EnableJpaRepositories
class Config {}
----
+
or via <<repositories.create-instances,XML configuration>>:
+
[source, xml]
----
<?xml version="1.0" encoding="UTF-8"?>
@ -148,13 +153,15 @@ or via <<repositories.create-instances,XML configuration>>: @@ -148,13 +153,15 @@ or via <<repositories.create-instances,XML configuration>>:
</beans>
----
+
The JPA namespace is used in this example. If you are using the repository abstraction for any other store, you need to change this to the appropriate namespace declaration of your store module which should be exchanging `jpa` in favor of, for example, `mongodb`.
+
Also, note that the JavaConfig variant doesn't configure a package explictly as the package of the annotated class is used by default. To customize the package to scan use one of the `basePackage…` attribute of the data-store specific repository `@Enable…`-annotation.
. Get the repository instance injected and use it.
+
[source, java]
----
public class SomeClient {
@ -578,8 +585,9 @@ public class MyRepositoryImpl<T, ID extends Serializable> @@ -578,8 +585,9 @@ public class MyRepositoryImpl<T, ID extends Serializable>
private final EntityManager entityManager;
public MyRepositoryImpl(Class<T> domainClass, EntityManager entityManager) {
super(domainClass, entityManager);
public MyRepositoryImpl(JpaEntityInformation entityInformation,
EntityManager entityManager) {
super(entityInformation, entityManager);
// Keep the EntityManager around to used from the newly introduced methods.
this.entityManager = entityManager;
@ -592,6 +600,8 @@ public class MyRepositoryImpl<T, ID extends Serializable> @@ -592,6 +600,8 @@ public class MyRepositoryImpl<T, ID extends Serializable>
----
====
WARNING: The class needs to have a constructor of the super class which the store-specific repository factory implementation is using. In case the repository base class has multiple constructors, override the one taking an `EntityInformation` plus a store specific infrastructure object (e.g. an `EntityManager` or a template class).
The default behavior of the Spring `<repositories />` namespace is to provide an implementation for all interfaces that fall under the `base-package`. This means that if left in its current state, an implementation instance of `MyRepository` will be created by Spring. This is of course not desired as it is just supposed to act as an intermediary between `Repository` and the actual repository interfaces you want to define for each entity. To exclude an interface that extends `Repository` from being instantiated as a repository instance, you can either annotate it with `@NoRepositoryBean` (as seen above) or move it outside of the configured `base-package`.
The final step is to make the Spring Data infrastructure aware of the customized repository base class. In JavaConfig this is achieved by using the `repositoryBaseClass` attribute of the `@Enable…Repositories` annotation:

10
src/main/java/org/springframework/data/repository/core/support/RepositoryFactorySupport.java

@ -335,8 +335,16 @@ public abstract class RepositoryFactorySupport implements BeanClassLoaderAware { @@ -335,8 +335,16 @@ public abstract class RepositoryFactorySupport implements BeanClassLoaderAware {
Constructor<?> constructor = ReflectionUtils.findConstructor(baseClass, constructorArguments);
if (constructor == null) {
List<Class<?>> argumentTypes = new ArrayList<Class<?>>(constructorArguments.length);
for (Object argument : constructorArguments) {
argumentTypes.add(argument.getClass());
}
throw new IllegalStateException(String.format(
"No suitable constructor found on %s to match the given arguments: %s", baseClass, constructorArguments));
"No suitable constructor found on %s to match the given arguments: %s. Make sure you implement a constructor taking these",
baseClass, argumentTypes));
}
return (R) BeanUtils.instantiateClass(constructor, constructorArguments);

27
src/test/java/org/springframework/data/repository/core/support/RepositoryFactorySupportUnitTests.java

@ -49,7 +49,9 @@ import org.springframework.data.domain.Sort; @@ -49,7 +49,9 @@ import org.springframework.data.domain.Sort;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.repository.Repository;
import org.springframework.data.repository.RepositoryDefinition;
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.query.RepositoryQuery;
import org.springframework.data.repository.sample.User;
@ -291,7 +293,7 @@ public class RepositoryFactorySupportUnitTests { @@ -291,7 +293,7 @@ public class RepositoryFactorySupportUnitTests {
}
/**
* @see @see DATACMNS-714
* @see DATACMNS-714
*/
@Test
public void wrapsExecutionResultIntoListenableFutureWithEntityCollectionIfConfigured() throws Exception {
@ -303,6 +305,24 @@ public class RepositoryFactorySupportUnitTests { @@ -303,6 +305,24 @@ public class RepositoryFactorySupportUnitTests {
expect(prepareConvertingRepository(reference).readAllByLastname("Foo"), reference);
}
/**
* @see DATACMNS-763
*/
@Test
@SuppressWarnings("rawtypes")
public void rejectsRepositoryBaseClassWithInvalidConstructor() {
RepositoryInformation information = mock(RepositoryInformation.class);
doReturn(CustomRepositoryBaseClass.class).when(information).getRepositoryBaseClass();
EntityInformation entityInformation = mock(EntityInformation.class);
exception.expect(IllegalStateException.class);
exception.expectMessage(entityInformation.getClass().getName());
exception.expectMessage(String.class.getName());
factory.getTargetRepositoryViaReflection(information, entityInformation, "Foo");
}
private ConvertingRepository prepareConvertingRepository(final Object expectedValue) {
when(factory.queryOne.execute(Mockito.any(Object[].class))).then(new Answer<Object>() {
@ -411,4 +431,9 @@ public class RepositoryFactorySupportUnitTests { @@ -411,4 +431,9 @@ public class RepositoryFactorySupportUnitTests {
@Async
ListenableFuture<List<User>> readAllByLastname(String lastname);
}
static class CustomRepositoryBaseClass {
public CustomRepositoryBaseClass(EntityInformation<?, ?> information) {}
}
}

Loading…
Cancel
Save