Spring Boot 2.5 和 Spring 数据:@NoRepositoryBean 多模块项目中的意外行为
Spring Boot 2.5 and Spring Data: @NoRepositoryBean unexpected behaviour in multi-module project
我在无法更改的遗留代码中遇到以下问题。我有一个多模块项目,它在公共模块中定义了一个 Spring 数据接口,如下所示:
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!
...
所以似乎发生的是 Spring Data 尝试为 MyEntity getOneAndCheck() 方法生成查询,尽管有 @NoRepositoryBean 注释。这在我要从 Spring 3 with Spring Data 迁移到 Spring Boot 2.5.
的应用程序中按预期工作
不确定所描述的行为是否与存在多个 Maven 模块以及存储库、实体和 DTO 位于不同模块中这一事实有关。不确定它当前使用 Spring 运行的方式和使用 Spring Boot 的运行方式之间是否应该有任何区别。但结果是,这个遗留应用程序中的所有数十个存储库都因上述异常而失败。
重要的是要提到 class 需要使用注释来调整扫描:
@SpringBootApplication(scanBasePackages = "...")
@EnableJpaRepositories(basePackages={"...", "..."})
@EntityScan(basePackages= {"...", "..."})
public class MyApp
{
public static void main(String[] args)
{
SpringApplication.run(MyApp.class, args);
}
}
不确定这些注解是否应该从@NoRepositoryBean 的角度改变任何东西,但是当我添加这个 Spring Boot main class 时问题就出现了。以前没有 Spring Boot.
它工作正常
有什么建议吗?
非常感谢。
亲切的问候,
西摩
有两个东西一起玩:
- Spring 数据的默认自定义实现
- 存储库片段
None 个适用,因为:
- 默认自定义实现遵循实际存储库的名称。在您的例子中,实现名为
MyCustomRepositoryImpl
,而存储库名称为 MyRepository
。将实施重命名为 MyRepositoryImpl
将解决问题
- 自 Spring Data 2.0 以来,存储库检测将在存储库级别定义的接口视为片段候选者,其中每个接口都可以贡献一个片段实现。虽然实现名称遵循片段接口名称 (
MyCustomRepository
-> MyCustomRepositoryImpl
),但仅考虑没有 @NoRepositoryBean
的接口。
您有三个选择:
- 将您的自定义方法提取到它自己的片段接口中并提供片段名称后面的实现 class:
interface MyCustomFragement {
MyEntity getOneAndCheck();
}
class MyCustomFragementImpl implements MyCustomFragement {
public MyEntity getOneAndCheck() {…}
}
public interface MyRepository extends MyCustomRepository<MyEntity, Integer>, MyCustomFragment {…}
- 通过
@EnableJpaRepositories(repositoryBaseClass = …)
将 repositoryBaseClass
设置为实现自定义方法的 class。
- 如果您无法更改现有代码,您可以实施
BeanPostProcessor
来检查和更新 JpaRepositoryFactoryBean
的 bean 定义,方法是更新 repositoryFragments
并自行添加实施。此路径相当复杂并且需要使用反射,因为未公开 bean 工厂内部结构。
我在无法更改的遗留代码中遇到以下问题。我有一个多模块项目,它在公共模块中定义了一个 Spring 数据接口,如下所示:
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!
...
所以似乎发生的是 Spring Data 尝试为 MyEntity getOneAndCheck() 方法生成查询,尽管有 @NoRepositoryBean 注释。这在我要从 Spring 3 with Spring Data 迁移到 Spring Boot 2.5.
的应用程序中按预期工作不确定所描述的行为是否与存在多个 Maven 模块以及存储库、实体和 DTO 位于不同模块中这一事实有关。不确定它当前使用 Spring 运行的方式和使用 Spring Boot 的运行方式之间是否应该有任何区别。但结果是,这个遗留应用程序中的所有数十个存储库都因上述异常而失败。
重要的是要提到 class 需要使用注释来调整扫描:
@SpringBootApplication(scanBasePackages = "...")
@EnableJpaRepositories(basePackages={"...", "..."})
@EntityScan(basePackages= {"...", "..."})
public class MyApp
{
public static void main(String[] args)
{
SpringApplication.run(MyApp.class, args);
}
}
不确定这些注解是否应该从@NoRepositoryBean 的角度改变任何东西,但是当我添加这个 Spring Boot main class 时问题就出现了。以前没有 Spring Boot.
它工作正常有什么建议吗?
非常感谢。
亲切的问候,
西摩
有两个东西一起玩:
- Spring 数据的默认自定义实现
- 存储库片段
None 个适用,因为:
- 默认自定义实现遵循实际存储库的名称。在您的例子中,实现名为
MyCustomRepositoryImpl
,而存储库名称为MyRepository
。将实施重命名为MyRepositoryImpl
将解决问题 - 自 Spring Data 2.0 以来,存储库检测将在存储库级别定义的接口视为片段候选者,其中每个接口都可以贡献一个片段实现。虽然实现名称遵循片段接口名称 (
MyCustomRepository
->MyCustomRepositoryImpl
),但仅考虑没有@NoRepositoryBean
的接口。
您有三个选择:
- 将您的自定义方法提取到它自己的片段接口中并提供片段名称后面的实现 class:
interface MyCustomFragement {
MyEntity getOneAndCheck();
}
class MyCustomFragementImpl implements MyCustomFragement {
public MyEntity getOneAndCheck() {…}
}
public interface MyRepository extends MyCustomRepository<MyEntity, Integer>, MyCustomFragment {…}
- 通过
@EnableJpaRepositories(repositoryBaseClass = …)
将repositoryBaseClass
设置为实现自定义方法的 class。 - 如果您无法更改现有代码,您可以实施
BeanPostProcessor
来检查和更新JpaRepositoryFactoryBean
的 bean 定义,方法是更新repositoryFragments
并自行添加实施。此路径相当复杂并且需要使用反射,因为未公开 bean 工厂内部结构。