有没有办法使用弹簧引导自动配置注册存储库基类?

2022-09-02 10:58:24

我们有一个基本的JPA存储库类,其中包含我们在项目中使用的一些其他实用程序方法。按照Spring Data JPA文档,我们创建了该类,并在配置类中使用@EnableJpaRepositories注释,如以下示例所示:

@Configuration
@EnableJpaRepositories(basePackageClasses = MyApplication.class,
    repositoryBaseClass = MyJpaRepositoryImpl.class)
public class SpringDataJpaMyRepositoryConfiguration {
}

我们还设置了 basePackageClasses 属性,以便找到我们的存储库,因为配置类不在应用程序根包中。一切都按预期工作,所以到目前为止没有问题。

现在我们想创建一个弹簧引导启动器,将存储库基类添加到我们的项目中,而无需进一步配置,但我们不知道如何做到这一点。如果我们使用 EnableJpaRepositories 注释创建一个自动配置类,该类设置了 repositoryBaseClass 属性,那么在用 @SpringBootApplication 注释的类下查找存储库的自动存储库查找策略将不再起作用。

我们不能使用 basePackageClasses 属性,因为我们不知道使用自动配置的项目的主类或包。

有什么办法可以做到这一点吗?也许通过在我们的自动配置中重新定义一些豆子?

理想的方法是允许设置存储库基类,而不必再次定义所有Spring Data JPA自动配置。


答案 1

这个问题当时让我发疯,所以我想我可以帮助你。

基本上,我们的想法是:

  • 为 Jpa 配置创建配置类
  • 添加@EntityScan和@EnableJpaRepositories引用与 basePackageClass 相同的配置类
  • 在自动配置中导入此配置类
  • 创建一个注释,然后可以在需要 Jpa 配置的地方重用该注释

在您的示例中,您将 Spring 应用程序类用作扫描的基础。

我已经提出了一个示例项目来POC的主要思想 https://github.com/rdlopes/custom-jpa-demo

在示例中,有一个 JPA 实体/存储库的项目公开了 JPA 配置:

   @Configuration
   @EntityScan(basePackageClasses = JpaConfiguration.class)
   @EnableJpaRepositories(basePackageClasses = JpaConfiguration.class,
                          repositoryBaseClass = BaseRepositoryImpl.class)
   public class JpaConfiguration {

   }

请注意存储库的通用实现,您需要显示一个特殊的签名:

@NoRepositoryBean
public class BaseRepositoryImpl<T, ID extends Serializable>
        extends SimpleJpaRepository<T, ID>
        implements BaseRepository<T, ID> {

    public BaseRepositoryImpl(JpaEntityInformation<T, ID> entityInformation, EntityManager entityManager) {
        super(entityInformation, entityManager);
    }

    @Override
    public String someCustomMethod(ID id) {
        return "Class for entity of id " + id + " is: " + getDomainClass().getSimpleName();
    }
}

然后,您可以按如下方式创建自动配置:

@Configuration
@ConditionalOnClass(CustomJpaRepositories.class)
@Import(JpaConfiguration.class)
public class JpaCustomAutoConfiguration {

}

提供注释以保持整洁,并在需要JPA的地方使用它:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface CustomJpaRepositories {
}

使用 JPA 类就像在调用 JPA 存储库时进行此注释一样简单:

@SpringBootApplication
@CustomJpaRepositories
public class CustomJpaSampleApplication {

    public static void main(String[] args) {
        SpringApplication.run(CustomJpaSampleApplication.class, args);
    }

    @Bean
    public CommandLineRunner dataInitializer(UserRepository userRepository) {
        return args -> {
            User user1 = new User();
            user1.setName("user 1");
            userRepository.save(user1);

            User user2 = new User();
            user2.setName("user 2");
            userRepository.save(user2);

            userRepository.findAll()
                          .forEach(user -> System.out.println(
                                  userRepository.someCustomMethod(user.getId())));
        };
    }
}

希望这有助于您度过挠头的时刻:-)


答案 2

编辑:我几乎重写了我的答案 - 我误解了原来的问题

这不是最好的解决方案,但我可以看到它工作的唯一方法是 在里面使用SpEL 。@EnableJpaRepositories

然后,这可以进入您的自动配置,并仅在设置了基本包属性时才用于自动配置。@ConditionalOnProperty

@Configuration
@ConditionalOnProperty("repositories-base-packages")
public class BaseRepositoryAutoConfiguration {

    @Configuration
    @EnableJpaRepositories(
            repositoryBaseClass = MyJpaRepositoryImpl.class,
            basePackages = "${repositories-base-packages}"
    )
    public static class JpaRepositoriesConfig { }

}

然后,确保在应用程序中定义了 or。application.propertiesapplication.ymlrepositories-base-packages

不知道如何声明多个基本包,我的SpEL知识是原始的,所以不确定它是否可能。


推荐