Erik Hofer
Erik Hofer

Reputation: 2656

@Primary equivalent for autowired Spring JPA repositories

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

Answers (3)

Maurice
Maurice

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

Erik Hofer
Erik Hofer

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

Ralph
Ralph

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

Related Questions