27

Given the list of all spring data repositories in some class Bar:

@Autowired
private List<Repository> repositories;

How can I find the repository for an existing domain class Foo in the above list?

Assuming that the following exists:

@Entity
public class Foo {
  ...
}

and

public interface FooRepository extends JpaRepository<Foo, String> {}
Oliver Drotbohm
  • 80,157
  • 18
  • 225
  • 211
Udo
  • 2,300
  • 1
  • 23
  • 27

6 Answers6

27

Spring Data Commons contains a class Repositories that takes a ListableBeanFactory to find all repository beans defined in it and exposes an API to obtain these instances by domain class (through ….getRepository(Class<?> type)).

This class should be used with care. As there's some serious proxy generation going on for the repository instances you have to make sure the Repositories instance is created as late as possible during the ApplicationContext creation. The preferred way is to implement ApplicationListener and create the instance by listening to the ContextRefreshedEvent.

In case you're writing a web application, the safest way to use Repositories is by bootstrapping the repositories in the ApplicationContext created by the ContextLoaderListener and place the Repositories (see the reference documentation of Spring MVC for details.

Oliver Drotbohm
  • 80,157
  • 18
  • 225
  • 211
21
@Service
public class GenericRepository {

    @Autowired
    private WebApplicationContext appContext;

    Repositories repositories = null;

    public GenericRepository() {
        repositories = new Repositories(appContext);
    }

    public JpaRepository getRepository(AbstractPersistable entity) {
        return (JpaRepository) repositories.getRepositoryFor(entity.getClass());
    }

    public Object save(AbstractPersistable entity) {
        return getRepository(entity).save(entity);
    }

    public Object findAll(AbstractPersistable entity) {
        return getRepository(entity).findAll();
    }

    public void delete(AbstractPersistable entity) {
        getRepository(entity).delete(entity);
    }
}
dakab
  • 5,379
  • 9
  • 43
  • 67
Deepak
  • 1,670
  • 1
  • 20
  • 20
3

The key to the solution is Spring's org.springframework.data.repository.core.support.DefaultRepositoryMetadata which provides the method getDomainType().

DefaultRepositoryMetadata needs the repository interface as constructor arg. So one can loop over all existing repositories, retrieve the repository interface (which is still a tricky part because the repository instance has more than one interface) and find the one where getDomainType()equals Foo.class.

Udo
  • 2,300
  • 1
  • 23
  • 27
1

You can use Spring's GenericTypeResolver to get the Entity class from your Repository.

Repository<Foo, String> fooRepository = repositories.stream()
    .filter(repository -> GenericTypeResolver
        .resolveTypeArguments(repository.getClass(), Repository.class)[0].equals(Foo.class))
    .findFirst().get();
ekcrisp
  • 1,967
  • 1
  • 18
  • 26
1

This worked for me :

Repositories repositories = new Repositories(context);
CrudRepository repo = (CrudRepository) repositories.
            getRepositoryFor(entityClass).get();
menkaix
  • 11
  • 2
1

I will give my opinion based on my recent experience to make reflection in some classes or interfaces in Spring Boot.

As @ekcrisp said in his answer above:

You can use Spring's GenericTypeResolver to get the Entity class from your Repository.

My experience: unfortunately the common ways of reflection don't work to get the <T> entity class in a Spring Boot's repository. I tried in a lot of maneers. But this GenericTypeResolver actually could get the generics <T> type from JpaRepository<T, ID>.

In other words, the function below can provide all generic types of a class as an array. Then you can choose one of them:

public Class<?>[] getGenericType( Class<?> classInstance, Class<?> classToGetGenerics ) {
    return GenericTypeResolver.resolveTypeArguments( classInstance, classToGetGenerics );
}

This answer is about this stack and its versions:

JDK 11, Spring Boot 2.7.11 with Spring MVC, Spring Data JPA, and others libs. Posted in 2023-05-11.


If you want to add a solution in your "ObjectUtil" local solution, maybe these methods can be useful to get generic types, in any position, like get <T> or <ID> in JpaRepository<T, ID>:

@UtilityClass
public class ObjectUtil {
    // ...

    /**
     *
     * @param classInstance
     * @param classToGetGenerics
     *
     * @return the generic classes of the given param, based on class to get generics param.
     *
     * @see GenericTypeResolver#resolveTypeArguments
     */
    public static Class<?>[] getGenericType( Class<?> classInstance, Class<?> classToGetGenerics ) {
        return GenericTypeResolver.resolveTypeArguments( classInstance, classToGetGenerics );
    }

    /**
     *
     * @param classInstance
     * @param classToGetGenerics
     * @param genericPosition
     *
     * @return the generic class of the given param, based on class to get generics and generic position params.
     *
     * @see ObjectUtil#getGenericType
     * @see GenericTypeResolver#resolveTypeArguments
     */
    public static Class<?> getGenericType( Class<?> classInstance, Class<?> classToGetGenerics, int genericPosition ) {
        Class<?>[] typeArguments = getGenericType( classInstance, classToGetGenerics );
        if( typeArguments != null && typeArguments.length >= genericPosition ) {
            return typeArguments[genericPosition];
        }
        throw new IllegalArgumentException( "Could not determine generic type for interface " + classInstance.getName() );
    }
    
    // ... 
}

So we can use like the example below (I created a QueryUtil to handle specifically persistence stuff):

@UtilityClass
public class QueryUtil {

    // ...

    /**
     *
     * @param repositoryClass
     *
     * @return the @Entity class of the given repository param.
     *
     * @see ObjectUtil#getGenericType(Class, Class, int)
     * @see ObjectUtil#getGenericType(Class, Class)
     */
    public static Class<?> getRepositoryEntityType( Class<?> repositoryClass ) {
        return ObjectUtil.getGenericType( repositoryClass, Repository.class, 0 );
    }

}

I hope this is still useful. Here it was used in a customized audit logic, made by aspects, so that the Entity is audited.