Есть ли способ зарегистрировать базовый класс репозитория с автоматической конфигурацией загрузки spring?

У нас есть базовый класс репозитория JPA с некоторыми дополнительными утилитами, которые мы используем в наших проектах. После Spring Документация JPA данных > мы создали класс и использовали аннотацию @EnableJpaRepositories в классе конфигурации, как в следующем примере:

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

Мы также устанавливаем атрибут basePackageClasses, чтобы наши репозитории были найдены, так как класс конфигурации не находится в корневом пакете приложения. Все работает так, как ожидалось, поэтому проблем пока нет.

Теперь мы хотели бы создать стартер загрузки spring, чтобы добавить базовый класс репозитория к нашим проектам без дальнейшей настройки, но мы не знаем, как это сделать. Если мы создадим класс AutoConfiguration с параметром аннотации EnableJpaRepositories, атрибутом repositoryBaseClass, стратегия поиска в автоматическом репозитории, которая ищет репозитории в классе, аннотированном с помощью @SpringBootApplication, больше не работает.

И мы не можем использовать атрибут basePackageClasses, поскольку мы не знаем основной класс или пакет проекта, используя автоконфигурацию.

Есть ли способ сделать это? Может быть, переопределив некоторые bean в нашей автоконфигурации?

Идеальный способ - это то, что позволяет установить базовый класс репозитория без необходимости повторного определения всей автоконфигурации spring Data JPA.

Ответы

Ответ 1

РЕДАКТОР: Я почти переписал свой ответ - я неправильно понял исходный вопрос

Это не самое приятное решение, но единственный способ, которым я вижу это, - использовать SpEL внутри @EnableJpaRepositories.

Затем вы можете перейти в свою автоматическую конфигурацию и использовать @ConditionalOnProperty только для автоматической настройки, если установлено свойство базового пакета

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

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

}

Затем убедитесь, что у вас есть application.properties или application.yml, который определяет repositories-base-packages внутри вашего приложения.

Не уверен, как вы объявите несколько базовых пакетов, мои знания SpEL являются примитивными, поэтому не уверены, что это будет возможно.

Ответ 2

Этот вопрос заставлял меня сходить с ума в то время, поэтому я думал, что смогу помочь вам в этом.

В принципе, идея заключается в следующем:

  • Создайте класс конфигурации для вашей конфигурации 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())));
        };
    }
}

Надеюсь, это поможет вам пройти мимо царапин на голове: -)