【问题标题】:Create Custom Repository to Spring Data JPA为 Spring Data JPA 创建自定义存储库
【发布时间】:2018-12-25 09:41:36
【问题描述】:

我尝试按照本教程创建custom repositoryhttps://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

有很多类似的问题,但没有一个适合我。可能是当前版本 2.1.1 的 Spring 问题,或者我错过了一些配置。

【问题讨论】:

标签: java spring spring-boot spring-data-jpa spring-repositories


【解决方案1】:

要让您的测试正常运行,您必须执行以下操作:

1) 将StudentJPAH2ConfigbasePackages 的错误定义替换为com.example.demo.dao,或者最好将其删除为多余的:

@Configuration
@EnableJpaRepositories(repositoryBaseClass = ExtendedRepositoryImpl.class)
public class StudentJPAH2Config {
}

2) 还将@ComponentScan 中的basePackagesDemoApplication 类中的@EntityScan 替换为com.example.demo.daocom.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) 更正您的测试类 - 使用 @DataJpaTest 测试 DAO 层并导入您的 StudentJPAH2Config 配置:

@RunWith(SpringRunner.class)
@DataJpaTest
@Import(StudentJPAH2Config.class)
public class ExtendedStudentRepositoryIntegrationTest {
   //...
}

通过这些更正,我已成功运行您的测试。

【讨论】:

  • 我无法删除 @ComponentScan 和 @EntityScan,当我将示例项目导入多个模块项目时,它会出错 Field * in * required a bean of type * that could not be found。我的其他模块的另一个问题必须使用注释@EnableJpaRepositories(repositoryBaseClass = SimpleJpaRepository.class)@EnableJpaRepositories(repositoryBaseClass = ExtendedRepositoryImpl.class) 实现配置,否则会出错required a bean of type * that could not be found
【解决方案2】:

在我们运行测试之前,让我们确保主应用程序正在运行。实际上,它有一些问题。

1。 IllegalArgumentException:不是托管类型:类com.example.demo.entity.Student

问题是 @EntityScan("com.example.demo.entity.*")。包名称不正确,因此不会扫描 Student 类。

解决方案是@EntityScan("com.example.demo.entity")

2。 PropertyReferenceException:找不到类型 Student 的属性 attributeContainsText

问题在于存储库基类未设置JpaQueryLookupStrategy 认为它应该从方法名称findByAttributeContainsText 构造一个RepositoryQuery。它识别模式findBy{EntityPropertyName} 并未能在Student 中找到字段attributeContainsText

未设置存储库基类,因为未应用配置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。

解决办法是列出所有的配置类。

@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 sup> Hibernate 是 Spring 应用程序中的默认 JPA 提供程序。

【讨论】:

  • 我目前看到的最佳答案。谢谢!
【解决方案3】:

进行了以下更改:

@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 类

@Configuration
@ComponentScan("com.example.demo")
@EnableJpaRepositories(basePackages = "com.example.demo.dao",
        repositoryBaseClass = ExtendedRepositoryImpl.class)
public class StudentJPAH2Config {
    // additional JPA Configuration
}

缺少空构造函数的学生班级:

@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;
    }
}

结果

及应用程序运行状态

【讨论】:

  • @DarrenForsythe 你读过这个问题了吗??你在这里吹嘘最佳实践???你解决了这个问题吗???在否决答案之前仔细阅读问题。
  • 对,我也看了源码。也许阅读 Spring Boot 文档。这不是一个需要这种级别配置的 Spring Framework 应用程序,仅仅因为它可以工作并不意味着它是正确的。
  • @DarrenForsy 这些配置本来可以避免一点。但他的目标不是那个问题的目标。你说这些只是为了争论。坚持要帮助人们,而不仅仅是在这里和那里想知道。
  • 我说这些是为了阻止不良弹簧靴配置的扩散。你自己说他们可以避免。那么为什么不遵循最佳实践呢?它是一个 Spring Boot 应用程序,而不是 Spring 框架。
【解决方案4】:

您的自定义 Spring Data JPA 存储库基类的实现确实有效。 Spring Boot 应用程序上下文中只有几项需要解决。您确实将 Spring Boot Starter 模块声明为依赖项,因此我提交了一个 pull request,其中包含将手动配置上下文的所有责任转移到 Spring Auto Configuration 工厂提供程序的更改。

这里有一些关于使测试通过所需更改的注释,以及每项更改的一些简要细节。请注意,要在测试范围之外运行 Spring Boot 应用程序,您需要将适当的 JDBC 供应商依赖项添加到类路径,以及任何 Spring 应用程序属性以定义数据源属性。在定义数据源属性后,Spring Auto Config 应该通过合理的设置来处理任何 EntityManagers 和 TransactionManagers。见Spring Boot features - Working with SQL Databases

更改说明:

  • 删除了所有 Spring Configuration 注释(@SpringBootApplication@EnableJpaRepositories 除外)。应用程序 pom 被配置为依赖于 Spring Boot Starter 模块,这些模块已经通过 spring.factories 提供 Spring Auto Configuration 类,同时还确保满足所有其他依赖项。
  • @EnableJpaRepositories 不需要声明 basePackages,因为 Auto Config 模块将默认使用 Spring Main Application 类下的所有包。仍然需要声明额外的repositoryBaseClass
  • @ComponentScan 被删除,因为 Auto Config 模块将对 Spring Main Application 类下的所有包执行 ComponentScan,该类已经位于项目的顶层包中。此外,这是声明一个 basePackages 来扫描不包括 @ConfigurationStudentJPAH2Config 的值。
  • @EntityScan 被删除,因为 Auto Config 模块将对 Spring Main Application 类下的所有包执行扫描。
  • @EnableTransactionManagement 删除,因为自动配置模块将执行相同的操作。
  • 删除了默认的application.properties,因为它们依赖于H2,但是 pom 只在测试范围的类路径中提供了这种依赖关系。此外,属性文件中设置的所有配置属性通常由 Spring Starters 在 H2 存在时自动配置。
  • Student 上为休眠添加了默认构造函数。 Hibernate 在管理实体时需要此构造函数,除非使用了超出此问题范围的 @PersistenceConstructor@Immutable
  • ExtendedStudentRepositoryIntegrationTest 中的单元测试应用程序上下文配置了 Spring Boot 自动配置。这与DemoApplicationTests 中为 Spring Boot 集成测试定义的上下文配置相同。将 @ContextConfiguration 直接与 StudentJPAH2Config 一起使用时,在 Spring Boot Main Application 类(即 ComponentScan、EntityScan、TransactionManagement)上定义的配置未应用于 Spring Test Application Context。

关于 Spring Auto Configuration 的几点说明,因为它是阻碍该项目成功运行的主要问题:

Spring 应用程序启动器(通常)分为两种类型的依赖项。

  • autoconfigure 模块在其 POM 中声明了一组最小依赖项,但是在编译期间构建了许多依赖项。自动配置模块还通过位于包的META-INF 中的spring.factories 文件宣传Configuration 类和工厂。 Spring Boot 在它的引导阶段将加载这些配置类。最后,配置类将有选择地声明 bean 并根据上下文中声明的其他 bean 以及在类路径中实际找到的依赖项进行其他应用程序上下文更改/增强。这允许通用/合理的默认配置,所有这些都基于您要提取的库和最少的属性集。
  • starter 模块(通常)不提供太多/任何类。他们的目的是提供 POM 来声明特定 Spring 项目的良好开发基线所需的依赖关系。启动模块还依赖于适用的 autoconfigure 模块,这些模块与这些依赖项配对,让您可以快速开始运行您的应用程序。

在大多数情况下,使用 Spring Starters 时,主要驱动程序不应该弄清楚您需要在 Spring 上下文中手动配置什么,而是应该取消配置什么,以防其基线不适合您的需要.当涉及到像您一样在线学习教程/示例时,请记住,可能会显示一些围绕配置的项目。有时这些配置说明是必要的,但它们通常是为了显示非引导 Spring 应用程序中所需的配置或透明性的详细信息。

【讨论】:

    【解决方案5】:

    您需要在已实现的类中添加注释。 您必须添加 @Component 或 @Service 注释才能让 Spring 接收注入

    @Component
    public class ExtendedRepositoryImpl<T, ID extends Serializable>
    extends SimpleJpaRepository<T, ID> implements ExtendedRepository<T, ID> {
    

    【讨论】:

    • 通常这种错误与我给你的答案有关,我有时会得到它。请在 github 中查看一些官方文档或其他工作示例。您引用的页面并不总是为您提供完整的源代码,而是部分或少量的实现。
    【解决方案6】:

    您需要进行以下更改才能解决此问题。此外,我使用 JDK 8 执行此操作,因为我的本地没有 JDK 11。另外,为了重现问题,我执行了 ExtendedStudentRepositoryIntegrationTest.java。

    1. 在 StudentJPAH2Config.java 中,需要添加组件扫描。
    @Configuration
    @ComponentScan("com.example.demo")
    @EnableJpaRepositories(basePackages = "com.example.demo.dao", 
      repositoryBaseClass = ExtendedRepositoryImpl.class)
    public class StudentJPAH2Config {
    
    1. 在 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,尽管它不是必需的。

    现在,这解决了您面临的直接问题,但您仍然会受到其他一些非常直接的问题的欢迎,您可以从这里继续解决。

    【讨论】:

      【解决方案7】:

      您必须将@Primaryannotation 放在ExtendedRepositoryImpl 类上。它应该可以工作。

      Spring 基本上无法限定依赖项。 @Primary 将确保无论您在何处为 ExtendedRepository 接口注入依赖项,第一个合格的 bean(class) 将是 ExtendedRepositoryImpl

      【讨论】:

        【解决方案8】:

        您必须删除@ContextConfiguration(classes = { StudentJPAH2Config.class }) 并在您的测试类ExtendedStudentRepositoryIntegrationTest 上添加@SpringBootTestannotations,它将解决您当前遇到的错误NoSuchBeanDefinitionException

        但我又遇到如下错误:

        Caused by: java.lang.IllegalArgumentException: Not a managed type: class com.example.demo.entity.Student
        

        这是因为 Spring 无法扫描实体 Student

        为此,您必须在 DemoApplication 类上将 @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() 方法不是有效方法。

        【讨论】:

          猜你喜欢
          • 2018-10-06
          • 1970-01-01
          • 2012-04-12
          • 2012-06-02
          • 1970-01-01
          • 2016-01-08
          • 2014-12-25
          • 2021-04-01
          • 2012-05-18
          相关资源
          最近更新 更多