spring-boot - 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 Data 的默认自定义实现
- 存储库片段
这些都不适用,因为:
- 默认自定义实现遵循实际存储库的名称。在您的情况下,实现被命名
MyCustomRepositoryImpl
,而存储库名称是MyRepository
. 将实现重命名为MyRepositoryImpl
将解决该问题 - 从 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 {…}
- 将
repositoryBaseClass
via设置@EnableJpaRepositories(repositoryBaseClass = …)
为实现自定义方法的类。 - 如果您无法更改现有代码,则可以通过自己更新和添加实现来实现 a
BeanPostProcessor
来检查和更新 bean 定义。这条路径相当复杂,并且需要使用反射,因为 bean 工厂内部没有暴露。JpaRepositoryFactoryBean
repositoryFragments
推荐阅读
- java - 使用 NewInstance() 创建对象
- javascript - 有没有办法在 vue 的方法调用中传递反应变量?
- kubernetes - Kubernetes 工作节点状态 NotReady
- kotlin - 我如何对描述自身成员之间关系的代数数据类型进行建模,除非它是自相关的?
- ios - 个人小部件中的快捷方式执行
- jenkins - Kubernetes 在詹金斯管道内失败
- visual-studio - Xamarin“_AndroidXJetifyEmbeddedFiles”构建错误 Visual Studio 2019
- roblox - 为什么代码没有运行。它在我的循环中没有做任何事情。| 罗布洛克斯工作室
- ada - Ada:`limited interface`中`limited`的目的是什么
- python - 在 for 循环中将列附加到 Pandas DataFrame