0

I've been searching for a way to do this but can't quite find it.

Basically I'd like to make a generic repository I can inject anywhere. I have lots of cruds I need to make in this project and they are all pretty much the same.

I've tried to simply do

@Service
public class MyService<T> {
   
   @Autowired
   CrudRepository<T, Long> myRepository;

}

However that didn't work(it says it couldn't find that bean).

So next I tried to define a generic CrudRepository interfance like

@Repository
public interface MyRepository<T> extends CrudRepository<T, Long> {
}

And to inyect it again like

@Service
public class MyService<T> {
   
   @Autowired
   MyRepository<T> myRepository;

}

But that didn't work either. I get the following error:

Caused by: org.springframework.data.mapping.MappingException: Couldn't find PersistentEntity for type class java.lang.Object!

I'm a bit at a loss as to what to do next. Any help would be greatly appreciated.

Livb
  • 33
  • 5
  • dont declare MyService as @Service, and as an asbtract class. And add a constructor MyService( MyRepository myRepository). then create an implementation. – ggr Nov 05 '20 at 21:35
  • @Livb May be this [post](https://stackoverflow.com/questions/19417670/using-generics-in-spring-data-jpa-repositories) will help you – Lyes Nov 05 '20 at 21:36
  • Noted, thanks. But that didn't fix the issue with the repository. As for the other post. I've looked into it but it requires table inheritance of which I can't do for reasons – Livb Nov 05 '20 at 21:37

1 Answers1

4

I see a lot of issues with what you're trying to do here. Remember, when Spring initializes it's going to look for a bean of the type you've declared to inject. To do this, it needs to be a resolvable type, see https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/core/ResolvableType.html.

The spring data jpa repositories are already generic, you can extend this

public interface MyRepository<T> extends CrudRepository<T, Long>{}

public interface TheirRepository<T> extends MyRepository<T>{}

public interface AnotherRepository<T> extends TheirRepository<T>{}

as far as you want, but ultimately if you want to use it you'll have to declare one that conforms to your entity. So, no matter how you dice this, you're going to end up with having to declare something like

public interface MyGenericRepository<MyActualEntity>{}

To get away from this, I encourage you to go ahead and create all your repositories as you normally would, then make a service class that generically saves, updates, deletes the entities by looking up the repository for the type that your operating on. Something like this:

import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.support.Repositories;
import org.springframework.stereotype.Service;
import org.springframework.web.context.WebApplicationContext;

@Service
public class GenericPersistenceService {

  private final WebApplicationContext applicationContext;
  private Repositories repositories;

  public GenericPersistenceService(WebApplicationContext applicationContext) {
    repositories = new Repositories(applicationContext);
    this.applicationContext = applicationContext;
  }

  public <T> T save(T object) {
    Object repository = repositories.getRepositoryFor(object.getClass()).orElseThrow(
        () -> new IllegalStateException(
            "Can't find repository for entity of type " + object.getClass()));
    CrudRepository<T, Long> crudRepository = (CrudRepository<T, Long>) repository;

    return crudRepository.save(object);
  }
}

The repositories are populated when the service is created, then when you pass an entity to the save method, it looks up the repository for the given entity and performs the save operation.

lane.maxwell
  • 5,002
  • 1
  • 20
  • 30
  • Thanks. Would it be more performant to have one single service singleton like this for all cruds and have it look up for whatever repository it needs whenever it needs to save or to have several service singletons for each entity? Or would the performance difference between the two just not be noticeable at all? – Livb Nov 09 '20 at 19:13
  • From a performance perspective, given what you're trying to accomplish, this is going to be very little overhead. This simply looks up the repository for the given entity and considering that all the repositories are loaded into a Map when the service bean is created, it's negligible. It's not going to be as fast as having a service pre-defined with the requisite repository injected into it, but that would require you to create the repositories and services for each entity. I always create a service for each entity, but this approach provides a generic solution like you are looking for. – lane.maxwell Nov 09 '20 at 19:20