@Primary 等效于自动装配的 Spring JPA 存储库

Posted

技术标签:

【中文标题】@Primary 等效于自动装配的 Spring JPA 存储库【英文标题】:@Primary equivalent for autowired Spring JPA repositories 【发布时间】:2016-02-03 11:16:45 【问题描述】:

我在应用程序中使用 Spring JPA 存储库和实体。现在,在该应用程序的风格中,我需要扩展我的一个实体并提供扩展存储库。

对于我需要覆盖/扩展的所有其他 bean,我只需创建一个新实现并使用 @Primary 对其进行注释,这样它将自动装配而不是默认实现。

但是,对于存储库,这不起作用。我可以使用 @Primary 注释新存储库,但它没有任何效果(两个 bean 都已找到,因此无法自动装配)。这是有道理的,因为存储库是一个接口而不是一个实现,实现是由 Spring 动态提供的。

我能否以某种方式告诉 Spring(通过存储库上的注释或通过配置)使用哪个存储库?还是我必须像 Using @Primary in Spring Data JPA repositories 那样手动解决问题,或者我应该想出一种存储库提供程序而不是自动装配?

编辑让事情更清楚: 假设我有一个实体A

@Entity
public class A 
  @Id
  private long id;

及其存储库

public ARepository extends Repository<A, Long> 

现在我将它扩展到实体B

@Entity
public class B extends A 


public interface BRepository extends ARepository 

通常,根据文档,您使用这样的存储库:

@Autowired
private ARepository repository;

但是,这确实不起作用,因为现在有两个 ARepository 类型的 bean。对于我自己实现的 bean,我会在扩展类上使用 @Primary,但对于存储库,在编译时没有实现接口。

【问题讨论】:

为什么为该实体扩展实体和存储库需要您在第二段中描述的内容。 ...也许应该改写第 2 段 - 我(并且没有其他人接缝)了解您想要做什么/您的问题是什么。 @Ralph 我添加了一些示例代码来解释我的问题。还有一个链接问题,我尝试使用存储库工厂而不是正常的自动装配。 现在我明白了。 - 有趣的问题。 【参考方案1】:

我能以某种方式告诉 Spring(通过存储库上的注释或通过 配置)使用哪个存储库?

是的,你可以。首先,您为每个存储库类指定一个唯一的 bean 名称。

@Repository("myARepository")
public ARepository extends Repository<A, Long> 


@Repository("myBRepository")
public interface BRepository extends ARepository 

然后,当您使用 ARepository 作为类型进行自动装配时,您应该使用 @Qualifier 注释来告诉 Spring 您想要哪个存储库。

@Autowire
@Qualifier("myBRepository")
private ARepository repository;

这将自动连接BRepository

【讨论】:

【参考方案2】:

仅作记录,可以按照此处Using @Primary in Spring Data JPA repositories 的建议将@Primary 支持添加到JPA 存储库中

我对缺失代码的实现:

private boolean isSpringDataJpaRepository(Class<?> beanClass) 
      return JpaRepositoryFactoryBean.class.isAssignableFrom(beanClass);

我认为@Ralph 的答案更好,因为类型安全。

【讨论】:

【参考方案3】:

我会从这个答案中调整这个想法:https://***.com/a/27549198/280244 和这个 git 示例 https://github.com/netgloo/spring-boot-samples/tree/master/spring-boot-springdatajpa-inheritance/src/main/java/netgloo/models

引入一个标记为@NoRepositoryBean的通用抽象Repository

@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

现在你可以注入ARepositoryBRepository,而且都是类型保存!

【讨论】:

当然我读过你之前链接的问题,但我认为它不适用于我的情况。不过,您的代码示例非常有趣。如果您想将AB 一起使用,这是一个很好的解决方案。但是,就我而言,我想以@Autowire ARepository 实际上返回BRepository 的实现的方式“覆盖”实体,以便将实体创建为B,并且如果我想不必再次加载使用B 的功能。这将是传统 @Primary bean 的行为。 @zero_cool:仔细看看我的建议。关键是,ARepositoryBRepository 是空的。您可以按类型注入ARepositoryBRepository。当然BRepository 不会扩展ARepository,但我不认为这是必要的。 @zero_cool:但是我你永远不会再加载B实体,那么根本没有理由拥有BRepository。 - 或者你为什么需要它? 也许我的用例并不常见。如原始问题所述(可能不清楚),我得到了一个仅提供AARepository 的应用程序(以及您的建议AbstractARepository)。现在在应用程序的味道中,例如针对特定客户的定制,我需要将A 扩展到B(以及相应的存储库),以便A 在应用程序中完全被B 替换。在原始应用程序中,我按类型自动装配ARepository,现在必须返回BRepository 的实现实例,从而创建B 的实例而不是A 我意识到这确实是解决方案,只是缺少一个链接。存储库以这种方式工作正常。但是,如果我不希望 A 类型的实体出现在我的数据库中,我就不能在我的主应用程序中使用 repository.save(new A()); 创建它们。这将使存储库通过dtype 列区分类型。我需要有一种工厂,以我的口味返回B 的实例。我一直认为存储库有问题,但实际上我也需要替换实体实例。感谢拉尔夫的耐心:)

以上是关于@Primary 等效于自动装配的 Spring JPA 存储库的主要内容,如果未能解决你的问题,请参考以下文章

从头认识Spring-2.1 自动装配-byType

Spring注解驱动开发第20讲——使用@Autowired@Qualifier@Primary这三大注解自动装配组件,你会了吗?

Spring源码解析

Spring-处理自动装配的歧义性

自动装配

spring框架