为 Spring Data JPA 创建自定义存储库
Create Custom Repository to Spring Data JPA
我尝试按照本教程创建 custom repository
:https://www.baeldung.com/spring-data-jpa-method-in-all-repositories
我的应用构建失败并出现错误:
NoSuchBeanDefinitionException: No qualifying bean of type 'com.example.demo.dao.ExtendedStudentRepository' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
我的完整源代码:
https://github.com/sesong11/springjpa-custom-repo
有很多类似的问题,但 none 我已经解决了。可能是当前版本 2.1.1 的 Spring 问题,或者我遗漏了一些配置。
您需要在已实现的 class 中添加注释。
您必须添加 @Component 或 @Service 注释以便 Spring 获取注入
@Component
public class ExtendedRepositoryImpl<T, ID extends Serializable>
extends SimpleJpaRepository<T, ID> implements ExtendedRepository<T, ID> {
您需要进行以下更改才能解决此问题。另外,我用 JDK 8 执行了这个,因为我的本地没有 JDK 11。另外,为了问题重现,我执行了ExtendedStudentRepositoryIntegrationTest.java
- StudentJPAH2Config.java中需要添加组件扫描
@Configuration
@ComponentScan("com.example.demo")
@EnableJpaRepositories(basePackages = "com.example.demo.dao",
repositoryBaseClass = ExtendedRepositoryImpl.class)
public class StudentJPAH2Config {
- 在 DemoApplication 中,必须更正基本包名称。
@SpringBootApplication
@ComponentScan("com.example.demo.dao")
@EntityScan("com.example.demo.entity")
@EnableTransactionManagement
public class DemoApplication {
#1 更改的原因是在测试配置中没有组件扫描,spring 无法在正确的包中查找依赖项。 #2 是必需的,因为您的 Student.java 直接位于 com.example.demo.entity 包内,而不是它的任何子包中。您还可以在测试配置文件 StudentJPAH2Config 中指定 @EntityScan,但这不是必需的。
现在,这解决了您面临的直接问题,但您仍然会遇到其他一些非常简单的问题,您可以从这里开始。
您必须在 ExtendedRepositoryImpl
class 上添加 @Primary
注释。它应该有效。
Spring基本没法限定依赖。 @Primary
将确保在任何地方为 ExtendedRepository
接口注入依赖项,第一个合格的 bean(class) 将是 ExtendedRepositoryImpl
.
进行了以下更改:
@SpringBootApplication
@ComponentScan("com.example.demo.dao")
@EntityScan("com.example.demo.entity")
@EnableJpaRepositories(basePackages = "com.example.demo.dao",
repositoryBaseClass = ExtendedRepositoryImpl.class)
@EnableTransactionManagement
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
对于 StudentJPAH2Config class
@Configuration
@ComponentScan("com.example.demo")
@EnableJpaRepositories(basePackages = "com.example.demo.dao",
repositoryBaseClass = ExtendedRepositoryImpl.class)
public class StudentJPAH2Config {
// additional JPA Configuration
}
缺少空构造函数的学生 class:
@Entity
public class Student {
public Student() {
}
public Student(long id, String name) {
this.id = id;
this.name = name;
}
@Id
private long id;
private String name;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
结果
和应用程序运行状态
您的自定义 Spring Data JPA 存储库基础 class 的实现确实有效。只有少数项目需要解决 Spring 引导应用程序上下文。您确实将 Spring Boot Starter 模块声明为依赖项,因此我提交了一个 pull request 更改,将手动配置上下文的所有责任转移到 Spring 自动配置工厂提供程序。
以下是关于通过测试所需更改的一些说明,以及每个测试的一些简要细节。请注意,对于 运行 测试范围之外的 Spring 引导应用程序,您需要将适当的 JDBC 供应商依赖项添加到 class 路径,以及任何 spring 应用程序属性来定义数据源属性。 Spring Auto Config 应在定义数据源属性后处理具有合理设置的任何 EntityManagers 和 TransactionManagers。参见 Spring Boot features - Working with SQL Databases。
更改说明:
- 删除了所有 Spring 配置注释(
@SpringBootApplication
和 @EnableJpaRepositories
除外)。应用程序 pom 配置为依赖 Spring Boot Starter 模块,这些模块提供 Spring 自动配置 class 已经通过 spring.factories,同时还确保满足所有其他依赖项。
@EnableJpaRepositories
不需要声明 basePackages
,因为自动配置模块将默认使用 Spring 主应用程序 class 下的所有包。额外的声明 repositoryBaseClass
仍然是必要的。
@ComponentScan
已删除,因为自动配置模块将对 Spring 主应用程序 class 下的所有包执行 ComponentScan,这已经是项目的顶级包。此外,这是声明一个 basePackages
扫描值,该值将不包括 @Configuration
class StudentJPAH2Config
.
@EntityScan
已删除,因为自动配置模块将在 Spring 主应用程序 class. 下的所有包中执行扫描
@EnableTransactionManagement
已删除,因为自动配置模块将执行相同的操作。
- 删除了默认值
application.properties
,因为它们依赖于 H2
,但是 pom 仅在测试范围的 class 路径中提供该依赖性。此外,属性 文件中设置的所有配置属性通常由 Spring Starters 在存在 H2 时自动配置。
- 在
Student
上添加了休眠的默认构造函数。 Hibernate 在管理实体时需要此构造函数,除非正在使用 @PersistenceConstructor
或 @Immutable
,这超出了此问题的范围。
- 已为
ExtendedStudentRepositoryIntegrationTest
中的单元测试应用程序上下文配置 Spring 引导自动配置。这与 DemoApplicationTests
中为 Spring 引导集成测试定义的上下文配置相同。当 @ContextConfiguration
直接与 StudentJPAH2Config
一起使用时,在 Spring 引导主应用程序 class 上定义的配置(即 ComponentScan、EntityScan、TransactionManagement)未应用于Spring 测试应用程序上下文。
关于 Spring 自动配置的一些注意事项,因为它是阻碍该项目成功的主要问题 运行:
Spring 应用程序启动器(通常)分为两种类型的依赖项。
autoconfigure
模块在其 POM 中声明了最小的依赖集,但是在编译期间构建了很多依赖项。自动配置模块还通过位于包 META-INF
中的 spring.factories
文件通告 Configuration
classes 和工厂。 Spring 在引导阶段启动将加载这些配置 classes。最后,配置 classes 将根据在上下文中声明的其他 bean 以及在 class 路径上实际找到的依赖项,选择性地声明 bean 并创建其他应用程序上下文 changes/enhancements。这允许 general/reasonable 默认配置全部基于您要引入的库和最少的属性集。
starter
模块(通常)不提供 much/any classes。他们的目的是提供 POM,声明特定 Spring 项目的良好开发基线所需的依赖项。入门模块还依赖于适用的 autoconfigure
模块,这些模块与这些依赖项配对,使您可以快速启动 运行 您的应用程序。
在大多数情况下,当使用 Spring Starters 时,主要驱动程序不应该弄清楚你需要在 Spring 上下文中手动配置什么,而是你应该取消什么配置以防其基线不适合您的需求。当像您一样在线关注 tutorials/examples 时,请记住可能会显示一些围绕配置的项目。有时可能需要这些配置说明,但通常它们是为了显示非启动 Spring 应用程序或透明度所需配置的详细信息。
您必须删除 @ContextConfiguration(classes = { StudentJPAH2Config.class })
并在您的测试中添加 @SpringBootTest
注释 class ExtendedStudentRepositoryIntegrationTest
它将解决您目前遇到的错误 NoSuchBeanDefinitionException
。
但是我又遇到了这样的错误:
Caused by: java.lang.IllegalArgumentException: Not a managed type: class com.example.demo.entity.Student
这是因为 Spring 无法扫描实体 Student
。
为此,您必须在 DemoApplication
class 上将 @EntityScan("com.example.demo.entity.*")
更改为 @EntityScan("com.example.demo.entity")
。
再次出现错误:
Caused by: org.springframework.data.mapping.PropertyReferenceException: No property attributeContainsText found for type Student!
这是因为方法 findByAttributeContainsText()
不是有效方法。
在我们运行测试之前,让我们确保主应用程序是运行ning。实际上,它有一些问题。
1。 IllegalArgumentException
:不是托管类型:class com.example.demo.entity.Student
.
问题是@EntityScan("com.example.demo.entity.*")
。包名称不正确,因此 Student
class 未被扫描。
解决方案是@EntityScan("com.example.demo.entity")
。
2。 PropertyReferenceException
: 未找到 属性 attributeContainsText
类型 Student
!
问题是未设置存储库基础class,并且JpaQueryLookupStrategy
认为它应该构建一个RepositoryQuery
方法名称 findByAttributeContainsText
。它识别模式 findBy{EntityPropertyName}
,但无法在 Student
.
中找到字段 attributeContainsText
未设置存储库基础 class,因为未应用配置 StudentJPAH2Config
。如果无法扫描,则不会应用配置。
@ComponentScan("com.example.demo.dao")
不扫描 StudentJPAH2Config
所在的包。
解决方案是 @ComponentScan("com.example.demo")
,它将扫描 "com.example.demo"
和 "com.example.demo.dao"
包1.
现在,应用没问题了,让我们回到测试中。
3。 NoSuchBeanDefinitionException
:没有可用的 com.example.demo.dao.ExtendedStudentRepository
类型的合格 bean。
问题是 StudentJPAH2Config
不构成全部配置,并且缺少一些 bean。
解决方法是列出所有配置classes.
@ContextConfiguration(classes = { StudentJPAH2Config.class, DemoApplication.class })
或
@ContextConfiguration(classes = DemoApplication.class)
或
@SpringBootTest
4。 JpaSystemException
:实体com.example.demo.entity.Student
没有默认构造函数。
问题是 Hibernate2 需要 Student
:
的默认构造函数
@Entity
public class Student {
@Id
private long id;
private String name;
public Student() {}
public Student(int id, String name) {
this.id = id;
this.name = name;
}
// getters & setters
}
1 @ComponentScan("com.example.demo.dao")
是多余的,因为这个包将被扫描,因为 @SpringBootApplication
位于那里。
2 Hibernate 是 Spring 应用程序中的默认 JPA 提供程序。
要让您的测试工作,您必须执行以下操作:
1) 将StudentJPAH2Config
中basePackages
的不正确定义替换为com.example.demo.dao
,或者最好将其删除为冗余:
@Configuration
@EnableJpaRepositories(repositoryBaseClass = ExtendedRepositoryImpl.class)
public class StudentJPAH2Config {
}
2) 同时将 @ComponentScan
中的 basePackages
和 DemoApplication
中的 @EntityScan
class 替换为 com.example.demo.dao
和 com.example.demo.entity
。或者最好完全删除那些和 @EnableTransactionManagement
注释:
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
3) 添加一个no-argument constructor到实体Student
.
4) 更正您的测试 class - 使用 @DataJpaTest 测试 DAO 层并导入您的 StudentJPAH2Config
配置:
@RunWith(SpringRunner.class)
@DataJpaTest
@Import(StudentJPAH2Config.class)
public class ExtendedStudentRepositoryIntegrationTest {
//...
}
通过这些更正,我 运行 你的测试成功了。
我尝试按照本教程创建 custom repository
:https://www.baeldung.com/spring-data-jpa-method-in-all-repositories
我的应用构建失败并出现错误:
NoSuchBeanDefinitionException: No qualifying bean of type 'com.example.demo.dao.ExtendedStudentRepository' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
我的完整源代码: https://github.com/sesong11/springjpa-custom-repo
有很多类似的问题,但 none 我已经解决了。可能是当前版本 2.1.1 的 Spring 问题,或者我遗漏了一些配置。
您需要在已实现的 class 中添加注释。 您必须添加 @Component 或 @Service 注释以便 Spring 获取注入
@Component
public class ExtendedRepositoryImpl<T, ID extends Serializable>
extends SimpleJpaRepository<T, ID> implements ExtendedRepository<T, ID> {
您需要进行以下更改才能解决此问题。另外,我用 JDK 8 执行了这个,因为我的本地没有 JDK 11。另外,为了问题重现,我执行了ExtendedStudentRepositoryIntegrationTest.java
- StudentJPAH2Config.java中需要添加组件扫描
@Configuration @ComponentScan("com.example.demo") @EnableJpaRepositories(basePackages = "com.example.demo.dao", repositoryBaseClass = ExtendedRepositoryImpl.class) public class StudentJPAH2Config {
- 在 DemoApplication 中,必须更正基本包名称。
@SpringBootApplication @ComponentScan("com.example.demo.dao") @EntityScan("com.example.demo.entity") @EnableTransactionManagement public class DemoApplication {
#1 更改的原因是在测试配置中没有组件扫描,spring 无法在正确的包中查找依赖项。 #2 是必需的,因为您的 Student.java 直接位于 com.example.demo.entity 包内,而不是它的任何子包中。您还可以在测试配置文件 StudentJPAH2Config 中指定 @EntityScan,但这不是必需的。
现在,这解决了您面临的直接问题,但您仍然会遇到其他一些非常简单的问题,您可以从这里开始。
您必须在 ExtendedRepositoryImpl
class 上添加 @Primary
注释。它应该有效。
Spring基本没法限定依赖。 @Primary
将确保在任何地方为 ExtendedRepository
接口注入依赖项,第一个合格的 bean(class) 将是 ExtendedRepositoryImpl
.
进行了以下更改:
@SpringBootApplication
@ComponentScan("com.example.demo.dao")
@EntityScan("com.example.demo.entity")
@EnableJpaRepositories(basePackages = "com.example.demo.dao",
repositoryBaseClass = ExtendedRepositoryImpl.class)
@EnableTransactionManagement
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
对于 StudentJPAH2Config class
@Configuration
@ComponentScan("com.example.demo")
@EnableJpaRepositories(basePackages = "com.example.demo.dao",
repositoryBaseClass = ExtendedRepositoryImpl.class)
public class StudentJPAH2Config {
// additional JPA Configuration
}
缺少空构造函数的学生 class:
@Entity
public class Student {
public Student() {
}
public Student(long id, String name) {
this.id = id;
this.name = name;
}
@Id
private long id;
private String name;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
结果
和应用程序运行状态
您的自定义 Spring Data JPA 存储库基础 class 的实现确实有效。只有少数项目需要解决 Spring 引导应用程序上下文。您确实将 Spring Boot Starter 模块声明为依赖项,因此我提交了一个 pull request 更改,将手动配置上下文的所有责任转移到 Spring 自动配置工厂提供程序。
以下是关于通过测试所需更改的一些说明,以及每个测试的一些简要细节。请注意,对于 运行 测试范围之外的 Spring 引导应用程序,您需要将适当的 JDBC 供应商依赖项添加到 class 路径,以及任何 spring 应用程序属性来定义数据源属性。 Spring Auto Config 应在定义数据源属性后处理具有合理设置的任何 EntityManagers 和 TransactionManagers。参见 Spring Boot features - Working with SQL Databases。
更改说明:
- 删除了所有 Spring 配置注释(
@SpringBootApplication
和@EnableJpaRepositories
除外)。应用程序 pom 配置为依赖 Spring Boot Starter 模块,这些模块提供 Spring 自动配置 class 已经通过 spring.factories,同时还确保满足所有其他依赖项。 @EnableJpaRepositories
不需要声明basePackages
,因为自动配置模块将默认使用 Spring 主应用程序 class 下的所有包。额外的声明repositoryBaseClass
仍然是必要的。@ComponentScan
已删除,因为自动配置模块将对 Spring 主应用程序 class 下的所有包执行 ComponentScan,这已经是项目的顶级包。此外,这是声明一个basePackages
扫描值,该值将不包括@Configuration
classStudentJPAH2Config
.@EntityScan
已删除,因为自动配置模块将在 Spring 主应用程序 class. 下的所有包中执行扫描
@EnableTransactionManagement
已删除,因为自动配置模块将执行相同的操作。- 删除了默认值
application.properties
,因为它们依赖于H2
,但是 pom 仅在测试范围的 class 路径中提供该依赖性。此外,属性 文件中设置的所有配置属性通常由 Spring Starters 在存在 H2 时自动配置。 - 在
Student
上添加了休眠的默认构造函数。 Hibernate 在管理实体时需要此构造函数,除非正在使用@PersistenceConstructor
或@Immutable
,这超出了此问题的范围。 - 已为
ExtendedStudentRepositoryIntegrationTest
中的单元测试应用程序上下文配置 Spring 引导自动配置。这与DemoApplicationTests
中为 Spring 引导集成测试定义的上下文配置相同。当@ContextConfiguration
直接与StudentJPAH2Config
一起使用时,在 Spring 引导主应用程序 class 上定义的配置(即 ComponentScan、EntityScan、TransactionManagement)未应用于Spring 测试应用程序上下文。
关于 Spring 自动配置的一些注意事项,因为它是阻碍该项目成功的主要问题 运行:
Spring 应用程序启动器(通常)分为两种类型的依赖项。
autoconfigure
模块在其 POM 中声明了最小的依赖集,但是在编译期间构建了很多依赖项。自动配置模块还通过位于包META-INF
中的spring.factories
文件通告Configuration
classes 和工厂。 Spring 在引导阶段启动将加载这些配置 classes。最后,配置 classes 将根据在上下文中声明的其他 bean 以及在 class 路径上实际找到的依赖项,选择性地声明 bean 并创建其他应用程序上下文 changes/enhancements。这允许 general/reasonable 默认配置全部基于您要引入的库和最少的属性集。starter
模块(通常)不提供 much/any classes。他们的目的是提供 POM,声明特定 Spring 项目的良好开发基线所需的依赖项。入门模块还依赖于适用的autoconfigure
模块,这些模块与这些依赖项配对,使您可以快速启动 运行 您的应用程序。
在大多数情况下,当使用 Spring Starters 时,主要驱动程序不应该弄清楚你需要在 Spring 上下文中手动配置什么,而是你应该取消什么配置以防其基线不适合您的需求。当像您一样在线关注 tutorials/examples 时,请记住可能会显示一些围绕配置的项目。有时可能需要这些配置说明,但通常它们是为了显示非启动 Spring 应用程序或透明度所需配置的详细信息。
您必须删除 @ContextConfiguration(classes = { StudentJPAH2Config.class })
并在您的测试中添加 @SpringBootTest
注释 class ExtendedStudentRepositoryIntegrationTest
它将解决您目前遇到的错误 NoSuchBeanDefinitionException
。
但是我又遇到了这样的错误:
Caused by: java.lang.IllegalArgumentException: Not a managed type: class com.example.demo.entity.Student
这是因为 Spring 无法扫描实体 Student
。
为此,您必须在 DemoApplication
class 上将 @EntityScan("com.example.demo.entity.*")
更改为 @EntityScan("com.example.demo.entity")
。
再次出现错误:
Caused by: org.springframework.data.mapping.PropertyReferenceException: No property attributeContainsText found for type Student!
这是因为方法 findByAttributeContainsText()
不是有效方法。
在我们运行测试之前,让我们确保主应用程序是运行ning。实际上,它有一些问题。
1。 IllegalArgumentException
:不是托管类型:class com.example.demo.entity.Student
.
问题是@EntityScan("com.example.demo.entity.*")
。包名称不正确,因此 Student
class 未被扫描。
解决方案是@EntityScan("com.example.demo.entity")
。
2。 PropertyReferenceException
: 未找到 属性 attributeContainsText
类型 Student
!
问题是未设置存储库基础class,并且JpaQueryLookupStrategy
认为它应该构建一个RepositoryQuery
方法名称 findByAttributeContainsText
。它识别模式 findBy{EntityPropertyName}
,但无法在 Student
.
attributeContainsText
未设置存储库基础 class,因为未应用配置 StudentJPAH2Config
。如果无法扫描,则不会应用配置。
@ComponentScan("com.example.demo.dao")
不扫描 StudentJPAH2Config
所在的包。
解决方案是 @ComponentScan("com.example.demo")
,它将扫描 "com.example.demo"
和 "com.example.demo.dao"
包1.
现在,应用没问题了,让我们回到测试中。
3。 NoSuchBeanDefinitionException
:没有可用的 com.example.demo.dao.ExtendedStudentRepository
类型的合格 bean。
问题是 StudentJPAH2Config
不构成全部配置,并且缺少一些 bean。
解决方法是列出所有配置classes.
@ContextConfiguration(classes = { StudentJPAH2Config.class, DemoApplication.class })
或
@ContextConfiguration(classes = DemoApplication.class)
或
@SpringBootTest
4。 JpaSystemException
:实体com.example.demo.entity.Student
没有默认构造函数。
问题是 Hibernate2 需要 Student
:
@Entity
public class Student {
@Id
private long id;
private String name;
public Student() {}
public Student(int id, String name) {
this.id = id;
this.name = name;
}
// getters & setters
}
1 @ComponentScan("com.example.demo.dao")
是多余的,因为这个包将被扫描,因为 @SpringBootApplication
位于那里。
2 Hibernate 是 Spring 应用程序中的默认 JPA 提供程序。
要让您的测试工作,您必须执行以下操作:
1) 将StudentJPAH2Config
中basePackages
的不正确定义替换为com.example.demo.dao
,或者最好将其删除为冗余:
@Configuration
@EnableJpaRepositories(repositoryBaseClass = ExtendedRepositoryImpl.class)
public class StudentJPAH2Config {
}
2) 同时将 @ComponentScan
中的 basePackages
和 DemoApplication
中的 @EntityScan
class 替换为 com.example.demo.dao
和 com.example.demo.entity
。或者最好完全删除那些和 @EnableTransactionManagement
注释:
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
3) 添加一个no-argument constructor到实体Student
.
4) 更正您的测试 class - 使用 @DataJpaTest 测试 DAO 层并导入您的 StudentJPAH2Config
配置:
@RunWith(SpringRunner.class)
@DataJpaTest
@Import(StudentJPAH2Config.class)
public class ExtendedStudentRepositoryIntegrationTest {
//...
}
通过这些更正,我 运行 你的测试成功了。