Yes you can. Here's an example that uses annotation configuration for Spring
I found it best to organise each datasource into a different package.
My package structure is:
datasource
|__ converters <-- holds any custom attribute converters for JPA
|__ default <-- for default datasource
| |__ model <-- contains entities for default datasource
| |__ repository <-- contains repositories for default datasource
|__ anotherdatasource <-- for second datasource
|__ model <-- contains entities for second datasource
|__ repository <-- contains repositories for second datasource
Pick one of the datasources as the default and create a configuration class for it along the lines of...
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(entityManagerFactoryRef = "entityManagerFactory", basePackages = { "com.example.datasource.default.repository" })
public class JpaDefaultDatasourceConfig {
@Primary
@Bean(name = "dataSource")
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
@Primary
@Bean(name = "entityManagerFactory")
public LocalContainerEntityManagerFactoryBean entityManagerFactory(EntityManagerFactoryBuilder builder, @Qualifier("dataSource") DataSource dataSource) {
return builder.dataSource(dataSource).packages("com.example.datasource.default.model", "com.example.datasource.converters").persistenceUnit("mydefault").build();
}
@Primary
@Bean(name = "transactionManager")
public PlatformTransactionManager transactionManager(@Qualifier("entityManagerFactory") EntityManagerFactory entityManagerFactory) {
return new JpaTransactionManager(entityManagerFactory);
}
}
Then for each subsequent datasource create another configuration class along the lines of... (Note: the use of packages to separate entity/repository scanning and the naming convention used throughout)
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(entityManagerFactoryRef = "anotherEntityManagerFactory", transactionManagerRef = "anotherTransactionManager", basePackages = { "com.example.datasource.anotherdatasource.repository" })
public class JpaAnotherDatasourceConfig {
@Bean(name = "anotherDataSource")
@ConfigurationProperties(prefix = "another.datasource")
public DataSource anotherDataSource() {
return DataSourceBuilder.create().build();
}
@Bean(name = "anotherEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean anotherEntityManagerFactory(EntityManagerFactoryBuilder builder, @Qualifier("anotherDataSource") DataSource anotherDataSource) {
return builder.dataSource(anotherDataSource).packages("com.example.datasource.anotherdatasource.model", "com.example.datasource.converters").persistenceUnit("anotherName").build();
}
@Bean(name = "anotherTransactionManager")
public PlatformTransactionManager anotherTransactionManager(@Qualifier("anotherEntityManagerFactory") EntityManagerFactory anotherEntityManagerFactory) {
return new JpaTransactionManager(anotherEntityManagerFactory);
}
}
You can configure each datasource using the configuration prefix. For the two examples above you could configure them using
application.yml
## JPA configuration
# This is the configuration for default datasource created by spring
spring:
datasource:
url: jdbc:mysql://localhost/default
username: foo
password: bar
driverClassName: com.mysql.jdbc.Driver
test-on-borrow: true
test-while-idle: true
validation-query: select 1;
# maxActive: 1
# This is the configuration for an additional datasource which we will create ourselves
another:
datasource:
url: jdbc:mysql://localhost/another
username: foo
password: bar
driverClassName: com.mysql.jdbc.Driver
test-on-borrow: true
test-while-idle: true
validation-query: select 1;
You don't necessarily need to worry now about the names of the persistence units (although we did name them) because we've carefully separated entity managers to only look at their entities/repositories you can simply inject the applicable repository and use it without having to worry about it getting the wrong datasource. If you do need the persistence unit you can just ask for it by name @PersistenceUnit(name = "anotherDatasource")