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.
2 Answers
Answers 1
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.
Answers 2
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 :-)
0 comments:
Post a Comment