From a0bec0fce04af8c798aa269e61eafaa63541601c Mon Sep 17 00:00:00 2001 From: Karl Bennett Date: Fri, 27 Apr 2012 00:59:41 +0100 Subject: [PATCH] DATACMNS-159 - Improve reference documentation on repository extensions. Updated section covering adding custom behavior to all repositories to better match what is in the actual code base. --- src/docbkx/repositories.xml | 102 ++++++++++++++++++++++++++---------- 1 file changed, 73 insertions(+), 29 deletions(-) diff --git a/src/docbkx/repositories.xml b/src/docbkx/repositories.xml index 564b8cc6b..f682a6ae1 100644 --- a/src/docbkx/repositories.xml +++ b/src/docbkx/repositories.xml @@ -23,6 +23,15 @@ interfaces of Spring Data repositories in general for detailled information on the specific features of a particular store consult the later chapters of this document. + + + As this part of the documentation is pulled in from Spring Data + Commons we have to decide for a particular module to be used as example. + The configuration and code samples in this chapter are using the JPA + module. Make sure you adapt e.g. the XML namespace declaration, types to + be extended to the equivalents of the module you're actually + using. +
@@ -664,7 +673,8 @@ UserRepository repository = factory.getRepository(UserRepository.class); An interface declaring custom shared behaviour - public interface MyRepository<T, ID extends Serializable> + +public interface MyRepository<T, ID extends Serializable> extends JpaRepository<T, ID> { void sharedCustomMethod(ID id); @@ -672,59 +682,93 @@ UserRepository repository = factory.getRepository(UserRepository.class); Now your individual repository interfaces will extend this - intermediate interface to include the functionality declared. The second - step is to create an implementation of this interface that extends the - persistence technology specific repository base class which will act as - custom base class for the repository proxies then. + intermediate interface instead of the + Repository interface to include the + functionality declared. The second step is to create an implementation + of this interface that extends the persistence technology specific + repository base class which will then act as a custom base class for the + repository proxies. - If you're using automatic repository interface detection using - the Spring namespace using the interface just as is will cause Spring - to create an instance of MyRepository. - This is of course not desired as it just acts as intermediary between - Repository and the actual repository - interfaces you want to define for each entity. To exclude an interface - extending Repository from being - instantiated as repository instance annotate it with - @NoRepositoryBean. + The default behaviour 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 it's 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 extending + Repository from being instantiated as a + repository instance it can either be annotate it with + @NoRepositoryBean or moved out side of + the configured base-package. Custom repository base class - public class MyRepositoryImpl<T, ID extends Serializable> + +public class MyRepositoryImpl<T, ID extends Serializable> extends SimpleJpaRepository<T, ID> implements MyRepository<T, ID> { + private EntityManager entityManager; + + // There are two constructors to choose from, either can be used. + public MyRepositoryImpl(Class<T> domainClass, EntityManager entityManager) { + super(domainClass, entityManager); + + // This is the recommended method for accessing inherited class dependencies. + this.entityManager = entityManager; + } + public void sharedCustomMethod(ID id) { // implementation goes here } } - The last step to get this implementation used as base class for - Spring Data repositories is replacing the standard - RepositoryFactoryBean with a custom one using a - custom RepositoryFactory that in turn creates - instances of your MyRepositoryImpl class. + The last step is to create a custom repository factory to replace + the default RepositoryFactoryBean that will in + turn produce a custom RepositoryFactory. The new + repository factory will then provide your + MyRepositoryImpl as the implementation of any + interfaces that extend the Repository + interface, replacing the SimpleJpaRepository + implementation you just extended. Custom repository factory bean - public class MyRepositoryFactoryBean<T extends JpaRepository<?, ?> - extends JpaRepositoryFactoryBean<T> { + +public class MyRepositoryFactoryBean<R extends JpaRepository<T, I>, T, I extends Serializable> + extends JpaRepositoryFactoryBean<R, T, I> { - protected RepositoryFactorySupport getRepositoryFactory(…) { - return new MyRepositoryFactory(…); + protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) { + + return new MyRepositoryFactory(entityManager); } - private static class MyRepositoryFactory extends JpaRepositoryFactory{ + private static class MyRepositoryFactory<T, I extends Serializable> extends JpaRepositoryFactory { - public MyRepositoryImpl getTargetRepository(…) { - return new MyRepositoryImpl(…); + private EntityManager entityManager; + + public MyRepositoryFactory(EntityManager entityManager) { + super(entityManager); + + this.entityManager = entityManager; } - public Class<? extends RepositorySupport> getRepositoryClass() { - return MyRepositoryImpl.class; + protected Object getTargetRepository(RepositoryMetadata metadata) { + + return new MyRepositoryImpl<T, I>((Class<T>) metadata.getDomainClass(), entityManager); + } + + protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) { + + // The RepositoryMetadata can be safely ignored, it is used by the JpaRepositoryFactory + //to check for QueryDslJpaRepository's which is out of scope. + return MyRepository.class; } } }