【问题标题】:Spring Boot 2.5 and Spring Data: @NoRepositoryBean unexpected behaviour in multi-module projectSpring Boot 2.5 和 Spring Data:多模块项目中的@NoRepositoryBean 意外行为
【发布时间】:2021-10-02 12:19:04
【问题描述】:

我在无法更改的旧代码中遇到以下问题。我有一个多模块项目,它在 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-boot spring-data-jpa spring-data


    【解决方案1】:

    有两件事可以一起发挥作用:

    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 {…}
    
    • 通过@EnableJpaRepositories(repositoryBaseClass = …)repositoryBaseClass 设置为实现自定义方法的类。
    • 如果您无法更改现有代码,您可以通过更新repositoryFragments 并自己添加实现来实现BeanPostProcessor 来检查和更新JpaRepositoryFactoryBean 的bean 定义。此路径相当复杂,并且需要使用反射,因为没有暴露 bean 工厂内部。

    【讨论】:

    • 感谢您的解释。不确定是否已了解所有内容,但鉴于上述选项,我将进一步调查。只是说明命名问题(MyRepository 而不是 MyRepositoryImpl)只是一个错字。
    猜你喜欢
    • 2017-05-31
    • 1970-01-01
    • 2018-10-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-10
    • 1970-01-01
    • 2019-05-24
    相关资源
    最近更新 更多