Home > Enterprise >  spring boot custom starter, define entities in it, without using @EntityScan. Is it possible?
spring boot custom starter, define entities in it, without using @EntityScan. Is it possible?

Time:08-31

I'm making some tests using custom starters for spring boot. I managed to configure everything except the entities. I've tryed using @Import to load entities in the @AutoConfiguration class but this does not work. Instead if we use @EntityScan in the starter the entities are loaded, but if you import this starter and have entities in the project that depends on the starter you are forced to use @EntityScan also in it, and in my opinion this breaks the autoconfiguration meaning of the starter because when you import a starter you should do nothing in order to use it, yes you can override the default configuration but not forced to do anything maybe to declare some properties.

Example of autoconfiguration class in the starter:

@AutoConfiguration(after = JpaRepositoriesAutoConfiguration.class)
//@AutoConfigureAfter(JpaRepositoriesAutoConfiguration.class)
@EnableJpaRepositories(basePackages = "com.example.springbootstarterexample.repository")
@Import({SomeServiceImpl.class, SomeEntityController.class /*, SomeEntity.class NOT WORKING*/})
@EntityScan(basePackages = "com.example.springbootstarterexample.domain")
public class ExampleAutoConfiguration {

}

and then if you have entities in the consumer of the starter you have to do this if you have entities in it:

@SpringBootApplication
@EntityScan(basePackages = "com.example.springbootconsumer.model")
public class SpringBootConsumerApplication {

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

}

Otherwise we can remove @EntityScan from the starter and do this in the consumer:

@SpringBootApplication
@EntityScan(basePackages = {"com.example.springbootconsumer.model", "com.example.springbootstarterexample.domain"})
public class SpringBootConsumerApplication {

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

}

but this totaly brakes the autoconfiguration, because you have to know where the entities are in the starter in order to start the application. I've write an example if interested.

CodePudding user response:

You need to add @ComponentScan to configuration bean.

@AutoConfiguration(after = JpaRepositoriesAutoConfiguration.class)
//@AutoConfigureAfter(JpaRepositoriesAutoConfiguration.class)
@EnableJpaRepositories(basePackages = "com.example.springbootstarterexample.repository")
@ComponentScan("com.example.springbootstarterexample")
@EntityScan(basePackages = "com.example.springbootstarterexample.domain")
public class ExampleAutoConfiguration {

}

CodePudding user response:

An interesting problem, but if you had an agreed way with consumers of your library on how to share information where their entities are, like for example they would provide a META-INF/entityscanforyouapp file with a list of packages where they have their entities

Consumer 1 would create META-INF/entityscanforyouapp

com.consumerapp1.entities

Consumer 1 would create META-INF/entityscanforyouapp

com.consumerapp2.entities

You could discover all these files and then just create an EntityManagerFactory bean that would include all the discovered packages

@Bean
@Primary
public LocalContainerEntityManagerFactoryBean entityManagerFactory(EntityManagerFactoryBuilder factoryBuilder, DataSource dataSource) {
    Map<String, Object> vendorProperties = <... read the additional hibernate properties if you need...>;

    String[] packagesWithEntities = discoverAllThePackages();

    return factoryBuilder.dataSource(datasource).packages(packagesWithEntities).properties(vendorProperties).build();
}

This is kind of "rough and ready" but should provide a good starting point if you want to go that way.

  • Related