You have basically two options. The first one is to provide an implementation of each repository interface. This can be seen in your examples. In this case you can override the default implementation of a repository for one entity repo. This can be useful to enhance a special entity repository, e.g. for reportings, etc.
The other option is when you want to provide a base implementation for ALL repositories then you need to use base repository fragments. Repository fragments can be very useful when you want to provide only one base implementation for every custom repository. Let me show you an example:
CustomReadRepository:
@NoRepositoryBean
public interface CustomReadRepository<T> extends Repository<T, String> {
public List<T> someMethod();
public List<T> someHighPerformanceMethod();
}
Now let's say we want one interface which combines different repositories and call it DataRepository
.
DataRepository:
@NoRepositoryBean
public interface DataRepository<T>
extends CrudRepository<T, String>, CustomReadRepository<T> {
}
The following interface is now the specific repository for an entity which extends the overall repository
@Repository
public interface UserRepository extends DataRepository<UserEntity> {
}
or
@Repository
public interface OrderRepository extends DataRepository<OrderEntity> {
}
And now we want to provide only ONE implementation for all specific repositories (OrderRepository, UserRepository, etc.).
public class CustomReadRepositoryImpl<T> implements CustomReadRepository<T> {
private static final EntityPathResolver resolver = SimpleEntityPathResolver.INSTANCE;
private final JpaEntityInformation<T, ?> entityInformation;
private final EntityManager entityManager;
private final Querydsl entityManager;
public CustomReadRepositoryImpl(JpaEntityInformation<T, ?> entityInformation,
EntityManager entityManager) {
this.entityManager = entityManager;
this.entityInformation = entityInformation;
}
@Override
public List<T> someMethod() {
//do something and return result
}
@Override
public List<T> someHighPerformanceMethod() {
// do some high performance querying here
}
}
So what you basically can do here is to enhance your repositories and provide only one implementation which can be useful e.g. to override Spring's default implementations (in order to make it faster or whatever reason). You also can provide new custom repos and implementations.
To make your fragment implementation accessible in Spring you have to do the following:
CustomRepositoryFactoryBean:
public class CustomRepositoryFactoryBean<T extends Repository<S, I>, S, I>
extends JpaRepositoryFactoryBean<T, S, I> {
public CustomRepositoryFactoryBean(Class<? extends T> repositoryInterface) {
super(repositoryInterface);
}
protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) {
return new CustomRepositoryFactory(entityManager);
}
this bean should used in the Main class:
Main Class:
@EnableJpaRepositories(repositoryFactoryBeanClass = CustomRepositoryFactoryBean.class)
public class App {
}
and the implementation of the factory:
CustomRepositoryFactory:
public class CustomRepositoryFactory extends JpaRepositoryFactory {
private final EntityManager entityManager;
public CustomRepositoryFactory(EntityManager entityManager) {
super(entityManager);
this.entityManager = entityManager;
}
@Override
protected RepositoryFragments getRepositoryFragments(RepositoryMetadata metadata) {
RepositoryFragments fragments = super.getRepositoryFragments(metadata);
if (CustomReadRepository.class.isAssignableFrom(
metadata.getRepositoryInterface())) {
JpaEntityInformation<?, Serializable> entityInformation =
getEntityInformation(metadata.getDomainType());
Object customRepoFragment = getTargetRepositoryViaReflection(
CustomReadRepositoryImpl.class, entityInformation, entityManager);
fragments = fragments.append(RepositoryFragment.implemented(customRepoFragment));
}
return fragments;
}
If you want to see a kind of real example have a look on the question here.