0
votes

We are looking for a way for different modules to separately specify @ComponentScan, @EnableJpaRepositories, and EntityManagerFactory.setPackagesToScan.

We combine multiple code modules into our web application. In addition, we allow for customer specific extensions to the base code which can add additional packages. In my testing I found that I can add an additional java config class and the additional packages in the @ComponentScan and @EnableJpaRepositories are picked up. I am thinking that if I could use @EntityScan I would see similar behavior.

However, we are performing some customization in EntityManagerFactory so @EntityScan is no longer an option. I dont think we want to specify another EntityManagerFactory is each module. The method setPacakgesToScan performs a replacement of the packages (instead of adding to current list).

There have been many posting about the ability to problematically set the packagesToScan but that appears to increase complexity significantly.

Example base configuration class

@Configuration
@EnableJpaRepositories(basePackages = { 
        "a", "b", "c"
 }
,repositoryFactoryBeanClass = BaseRepositoryFactoryBean.class
)
@ComponentScan(basePackages = {
        "a", "b", "c"
}
)
public class BaseConfig {       
    @Bean
    public EntityManagerFactory entityManagerFactory() throws NamingException {
        LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();
        emf.setJtaDataSource(dataSource());
        emf.setPackagesToScan(new String[] {"a", "b", "c"})
    }
}

Example extension configuration class

@Configuration
@EnableJpaRepositories(basePackages = { 
        "d"
 }
,repositoryFactoryBeanClass = BaseRepositoryFactoryBean.class
)
@ComponentScan(basePackages = {
        "d"
}
)
public class ExtensionConfig {      
    @Bean
    public EntityManagerFactory entityManagerFactory() throws NamingException {
        LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();
        emf.setJtaDataSource(dataSource());
        emf.setPackagesToScan(new String[] {"d"})
    }
}

Is there another way to achieve this behavior?

Thanks.

1

1 Answers

1
votes

You can try following approach, though I'm not sure it's best one:

Create a holder class for packages list. It must be accessible by client extension modules:

public class EmfPackages {
    private final String[] packages;
    public EmfPackages(String[] packages) {
        this.packages = packages;
    }
    public String[] getPackages() {
        return this.packages;
    }
}

Then adjust both configuration classes:

public class BaseConfig {       
    @Bean
    public EMFPackages baseEmfPackages() {
        return new EmfPackages(new String[] {"a", "b", "c"});
    }    

    @Bean
    // both "holders" are now injected here
    // AFAIK this feature works in Spring 4+
    public EntityManagerFactory entityManagerFactory(List<EmfPackages> emfPackages) throws NamingException { 
        // actually this is Java 8+ style, adjust for lower versions if needed
        final String[] combinedPackages = emfPackages.stream()
            .flatMap(p -> Arrays.stream(p.getPackages()))
            .collect(Collectors.toList())
            .toArray(new String[0]{});

        LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();
        emf.setJtaDataSource(dataSource());
        emf.setPackagesToScan(combinedPackages)
    }    
}

public class ExtensionConfig {      
    @Bean
    public EmfPackages extendedEmfPackages() {
        return new EmfPackages(new String[] {"d"});
    }
}