Spring Jpa adding custom functionality to all repositories and at the same time other custom funcs to a single repository

jpa, spring, spring-data, spring-data-jpa

Solution

You just need to combine the approaches on the doc page you mentioned. Let `Car` be the entity you want to have a custom repository for.

`CommonCustomRepository` defines the methods added to all repos:

@NoRepositoryBean
public interface CommonCustomRepository<T, ID extends Serializable> extends JpaRepository<T, ID> {
    String getCustomValue();
}

The implementation for this repo:

public class CommonCustomRepositoryImpl<T, ID extends Serializable> extends SimpleJpaRepository<T, ID> implements CommonCustomRepository<T, ID> {

    public CommonCustomRepositoryImpl(Class<T> domainClass, EntityManager em) {
        super(domainClass, em);
    }

    public CommonCustomRepositoryImpl(JpaEntityInformation<T, ?> entityInformation,
            EntityManager entityManager) {
        super(entityInformation, entityManager);
    }

    @Override
    public String getCustomValue() {
        return "CustomValue";
    }

}

Custom methods for `CarRepository`

@NoRepositoryBean
public interface CustomCarRepository {

    public String getCustomCarValue();
}

Implementation of the custom car-related methods

public class CarRepositoryImpl implements CustomCarRepository {

    @PersistenceContext
    private EntityManager em;

    @Override
    public String getCustomCarValue() {
        return "CustomCarValue";
    }
}

The combined interface for `CarRepository`

public interface CarRepository extends CommonCustomRepository<Car, Long>, CustomCarRepository {
}

Custom repo factory, just like in the documentation

public class CustomRepositoryFactoryBean<R extends JpaRepository<T, I>, T, I extends Serializable> extends
    JpaRepositoryFactoryBean<R, T, I> {

    @Override
    protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) {

        return new CustomRepositoryFactory(entityManager);
    }

    private static class CustomRepositoryFactory<T, I extends Serializable> extends JpaRepositoryFactory {

        private EntityManager entityManager;

        public CustomRepositoryFactory(EntityManager entityManager) {
            super(entityManager);

            this.entityManager = entityManager;
        }

        @Override
        protected Object getTargetRepository(RepositoryMetadata metadata) {

            return new CommonCustomRepositoryImpl<T, I>((Class<T>) metadata.getDomainType(), entityManager);
        }

        @Override
        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 CommonCustomRepositoryImpl.class;
        }
    }
}

The final bit of configuration, just like in the docs

<jpa:repositories base-package="com.example" factory-class="com.example.CustomRepositoryFactoryBean"/>

Problem

Spring documentation here http://docs.spring.io/spring-data/data-jpa/docs/current/reference/html/repositories.html#repositories.custom-implementations gives example to add custom functionalities to all repositories or to a single repositories, not both. Let suppose I want to add some custom funcs to all repositories (using Custom Repository Factory Bean) and some other only to a single repositories (docs says to use a Custom Interface and a Custom Impl); how can I achieve this? Some example code where I added "setCurrentTenansInSession" method to all repositories; now I want to add a custom method, e.g. "newCustomMethod", to ona single repository (that is a MyJpaRepository, as for my custom repository factory). How do I do this? Custom behaviour interface: ``` @NoRepositoryBean public interface MyJpaRepository<T, ID extends Serializable> extends JpaRepository<T, ID> { public void setCurrentTenantInSession(Object object); } ``` Custom behaviour implementation: ``` public class MultiTenantSimpleJpaRepository<T, ID extends Serializable> extends SimpleJpaRepository<T, ID> implements MyJpaRepository<T, ID> { public void setCurrentTenantInSession(Object object) { //custom impl } } ``` Custom repository factory bean : ``` public class MultiTenantJpaRepositoryFactoryBean<T extends Repository<S, ID>, S, ID extends Serializable> extends JpaRepositoryFactoryBean<T, S, ID> { @Override protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) { return new MultiTenantJpaRepositoryFactory(entityManager); } } ``` And finally the custom repository factory : ``` public class MultiTenantJpaRepositoryFactory extends JpaRepositoryFactory { public MultiTenantJpaRepositoryFactory(EntityManager entityManager) { super(entityManager); } @Override protected JpaRepository<?, ?> getTargetRepository(RepositoryMetadata metadata, EntityManager entityManager) { final JpaEntityInformation<?, Serializable> entityInformation = getEntityInformation(metadata.getDomainType()); final SimpleJpaRepository<?, ?> repo = new MultiTenantSimpleJpaRepository(entityInformation, entityManager); repo.setLockMetadataProvider(LockModeRepositoryPostProcessor.INSTANCE.getLockMetadataProvider()); return repo; } @Override protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) { return MultiTenantSimpleJpaRepository.class; } } ```

Original source