3
votes

I have two datasources and two entityManagerFactory instance. I was trying to use the new feature of 3.1 (Bootstrapping JPA entityManagerFactory without persistence.xml by using packagesToScan property).

In order to use the right entity manager factory instance, i have to distinguish using Persistence unit name and defining the PU name in persistence.xml is stopping the spring package scanning feature.

How to give the PU name while using packagesToScan feature?

My question is more duplicate of Is there a way to give persistenceUnitName for Spring's LocalContainerEntityManagerFactoryBean without persistence.xml?

I couldn't find the answer or comment on the above post. So reposting as new question.

2

2 Answers

2
votes

If I understand your question correctly, you would like to set the name of the persistenceUnit backing an EntityManagerFactory, when defined without a persistence.xml?

When you declare the entityManagerFactory, there is a persistenceUnitName property that you can set. For example:

<bean id="entityManagerFactory"
    class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
  <property name="dataSource" ref="dataSource"/>

  <property name="persistenceUnitName" value="yourPersistenceUnitName"/>

  <property name="jpaVendorAdapter" ref="jpaVendorAdapter"/>
  <property name="packagesToScan">
    <list>
      <value>..</value>
      ...
    </list>
  </property>
</bean>     
2
votes

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")