首页 > 解决方案 > Spring Boot 2.5 和 Spring Data:多模块项目中的@NoRepositoryBean 意外行为

问题描述

我在无法更改的遗留代码中面临以下问题。我有一个多模块项目,它在 commons 模块中定义了一个 Spring Data 接口,如下所示:

package commons;
...
@NoRepositoryBean
public interface MyCustomRepository<P, I extends Number> extends JpaRepository<MyEntity, Integer>
{
  MyEntity getOneAndCheck();
}

在另一个模块中,我将此接口扩展如下:

package data;
...
@Repository
public interface MyRepository extends MyCustomRepository<MyEntity, Integer>
{
  ...
}

所以,我的想法是我不希望 Spring Data 为 MyEntity getOneAndCheck() 方法生成任何实现,因为它是这样实现的:

package data;
...
public class MyCustomRepositoryImpl implements MyCustomRepository
{
  ...
  @Override
  public MyEntity getOneAndCheck()
  {
    ...
  }
  ...
}

但是,当我启动应用程序时,出现以下异常:

...
Caused by: java.lang.IllegalArgumentException: Failed to create query for method public abstract MyEntity commons.MyCustomRepository.getOneAndCheck()! No property getOne found for type MyEntity!
...

因此,尽管有 @NoRepositoryBean 注释,但似乎 Spring Data 尝试为 MyEntity getOneAndCheck() 方法生成一个查询。这在我将从 Spring 3 与 Spring Data 迁移到 Spring Boot 2.5 的应用程序中按预期工作。

不确定所描述的行为是否与存在多个 Maven 模块以及存储库、实体和 DTO 位于不同模块中的事实有关。不确定当前使用 Spring 的运行方式与使用 Spring Boot 的运行方式之间是否应该有任何区别。但结果是这个遗留应用程序中的所有数十个存储库都失败了,并出现了上述异常。

可能很重要的一点是,主类需要使用注释来调整扫描:

@SpringBootApplication(scanBasePackages = "...")
@EnableJpaRepositories(basePackages={"...", "..."})
@EntityScan(basePackages= {"...", "..."})
public class MyApp
{
  public static void main(String[] args)
  {
    SpringApplication.run(MyApp.class, args);
  }
}

从@NoRepositoryBean 的角度不确定这些注释是否应该改变任何东西,但是一旦我添加了这个 Spring Boot 主类,问题就出现了。在没有 Spring Boot 的情况下,它以前可以正常工作。

请问有什么建议吗?

提前谢谢了。

亲切的问候,

西摩

标签: spring-bootspring-data-jpaspring-data

解决方案


有两件事可以一起玩:

  1. Spring Data 的默认自定义实现
  2. 存储库片段

这些都不适用,因为:

  1. 默认自定义实现遵循实际存储库的名称。在您的情况下,实现被命名MyCustomRepositoryImpl,而存储库名称是MyRepository. 将实现重命名为MyRepositoryImpl将解决该问题
  2. 从 Spring Data 2.0 开始,存储库检测将存储库级别定义的接口视为片段候选,其中每个接口都可以贡献一个片段实现。虽然实现名称跟在片段接口名称 ( MyCustomRepository-> MyCustomRepositoryImpl) 之后,但只@NoRepositoryBean考虑没有的接口。

你有三个选择:

  • 将您的自定义方法提取到它自己的片段接口中,并提供一个遵循片段名称的实现类:
interface MyCustomFragement {
  MyEntity getOneAndCheck();
}

class MyCustomFragementImpl implements MyCustomFragement {
  public MyEntity getOneAndCheck() {…}
}

public interface MyRepository extends MyCustomRepository<MyEntity, Integer>, MyCustomFragment {…}
  • repositoryBaseClassvia设置@EnableJpaRepositories(repositoryBaseClass = …)为实现自定义方法的类。
  • 如果您无法更改现有代码,则可以通过自己更新和添加实现来实现 aBeanPostProcessor来检查和更新 bean 定义。这条路径相当复杂,并且需要使用反射,因为 bean 工厂内部没有暴露。JpaRepositoryFactoryBeanrepositoryFragments

推荐阅读