Reputation: 2656
I use Spring JPA repositories and entities in an application. Now, in a flavor of that application, I need to extend one of my entities and also provide an extended repository.
For all other beans I need to override/extend I simply create a new implementation and annotate it with @Primary so it will be autowired instead of the default implementation.
For repositories, however, this does not work. I can annotate the new repository with @Primary but it has no effect (both beans are found and can thus not be autowired). This makes sense because the repository is an interface and not an implementation, the implementation is provided by Spring dynamically.
Can I somehow tell Spring (via annotation on the repository or via configuration) which repository to use? Or do I have to do a manual workaround like this Using @Primary in Spring Data JPA repositories or should I come up with a kind of repository provider instead of autowiring?
Edit to make things clearer:
Let's say I have an entity A
@Entity
public class A {
@Id
private long id;
}
and its repository
public ARepository extends Repository<A, Long> {
}
Now I extend it to the entity B
@Entity
public class B extends A {
}
public interface BRepository extends ARepository {
}
Normally, as per the documentation, you use repositories like this:
@Autowired
private ARepository repository;
This does, however, not work because there are now two beans of the type ARepository
. For beans that I implement myself I would use @Primary
on the extending class but for repositories there is no implementation of the interface at compile time.
Upvotes: 5
Views: 2553
Reputation: 7381
Can I somehow tell Spring (via annotation on the repository or via configuration) which repository to use?
Yes you can. First you give each repository class a unique bean name.
@Repository("myARepository")
public ARepository extends Repository<A, Long> {
}
@Repository("myBRepository")
public interface BRepository extends ARepository {
}
Then when you autowire using ARepository
as a type you should use the @Qualifier
annotation to tell Spring which of the repositories you want.
@Autowire
@Qualifier("myBRepository")
private ARepository repository;
This will autowire a BRepository
Upvotes: 0
Reputation: 2656
Just for the record, it is possible to add @Primary
support to JPA repositories as suggested here Using @Primary in Spring Data JPA repositories
My implementation of the missing code:
private boolean isSpringDataJpaRepository(Class<?> beanClass) {
return JpaRepositoryFactoryBean.class.isAssignableFrom(beanClass);
}
I think the answer of @Ralph is better because of the type safety.
Upvotes: 1
Reputation: 120811
I would adapt the idea form this answer: https://stackoverflow.com/a/27549198/280244 and this git example https://github.com/netgloo/spring-boot-samples/tree/master/spring-boot-springdatajpa-inheritance/src/main/java/netgloo/models
Introduce a common abstract Repository that is marked with @NoRepositoryBean
@NoRepositoryBean
public interface AbstractARepository<T extends A>
extends Repository<T, Long> {
T findByName(String name); //or what ever your queries are
}
public ARepository extends AbstractARepository<A> {
//almost emtpy
}
public BRepository extends AbstractARepository<B> {
//queries that are special for B
}
Now you can inject ARepository
and BRepository
, and both are type save!
Upvotes: 5