Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to register a repository base class with a spring boot auto configuration?

We have a base JPA repository class with some additional utility methods that we use in our projects. Following the Spring Data JPA documentation we created the class and use the @EnableJpaRepositories annotation in a configuration class as in the following example:

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

We also set the basePackageClasses attribute so our repositories are found, as the configuration class is not in the application root package. Everything works as expected, so no problems so far.

Now we would like to create a spring boot starter to add the repository base class to our projects without further configuration, but we don't know how to do it. If we create an AutoConfiguration class with the EnableJpaRepositories annotation setting the repositoryBaseClass attribute, the automatic repository lookup strategy which looks for repositories under the class annotated with @SpringBootApplication doesn't work anymore.

And we can't use the basePackageClasses attribute as we don't know the main class or package of the project using the autoconfiguration.

Is there any way to do this? Maybe by redefining some bean in our autoconfiguration?

The ideal way would be something that allows to set the repository base class without having to define all the Spring Data JPA autoconfiguration again.

like image 704
Cèsar Avatar asked Feb 09 '17 09:02

Cèsar


People also ask

How do I auto-configure in spring boot?

Auto-Configuration in Spring BootThe annotation @EnableAutoConfiguration is used to enable the auto-configuration feature. The @EnableAutoConfiguration annotation enables the auto-configuration of Spring ApplicationContext by scanning the classpath components and registering the beans.

What is the process of registering a custom autoconfiguration with spring boot?

In order to create a custom auto-configuration, we need to create a class annotated as @Configuration and register it. Let's create a custom configuration for a MySQL data source: @Configuration public class MySQLAutoconfiguration { //... } Next, we need to register the class as an auto-configuration candidate.

What can be used to opt in auto-configuration in spring boot?

You need to opt-in to auto-configuration by adding the @EnableAutoConfiguration or @SpringBootApplication annotations to one of your @Configuration classes. You should only ever add one @EnableAutoConfiguration annotation. We generally recommend that you add it to your primary @Configuration class.


2 Answers

This question has driven me crazy at the time, so I thought I could help you on this.

Basically, the idea is to:

  • Create a configuration class for your Jpa config
  • Add @EntityScan and @EnableJpaRepositories referencing the same configuration class as the basePackageClass
  • Import this configuration class in your autoconfiguration
  • Create an annotation that you can then reuse where you need your Jpa config

In your example, you're using your Spring application class as your base for scannning.

I've put up a sample project to POC the main ideas at https://github.com/rdlopes/custom-jpa-demo

In the example, there's a project for the JPA entities/repositories exposing a JPA configuration:

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

   }

Be careful with the common implementation for your repositories, you need to show a special signature:

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

You can then create your auto configuration as such:

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

}

Providing an annotation to keep things tidy and use it where you need JPA:

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

Using your JPA classes will be as simple as having this annotation where you call your JPA repositories:

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

Hope this helps you getting passed the head scratching moments :-)

like image 151
rdlopes Avatar answered Sep 22 '22 13:09

rdlopes


EDIT: I've pretty much rewritten my answer - I misunderstood the original question

It's not the nicest solution but the only way I can see this working is by using SpEL inside @EnableJpaRepositories.

This can then go in your auto-configuration and use @ConditionalOnProperty to only auto-configure if the base package property is set

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

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

}

Then make sure you have a application.properties or application.yml which defines repositories-base-packages inside your application.

Not sure how you'd declare multiple base packages, my SpEL knowledge is primitive so not sure if it would even be possible.

like image 26
dan.jones Avatar answered Sep 19 '22 13:09

dan.jones