I have researched and found an explaination and sample code as to how to use spring data jpa with multiple datasources which refers to configuring multiple jpa:repositories in the xml configuration as follows:
<jpa:repositories base-package="org.springframework.data.jpa.repository.sample"
entity-manager-factory-ref="entityManagerFactory">
<repository:exclude-filter type="assignable" expression="org.springframework.data.jpa.repository.sample.AuditableUserRepository" />
</jpa:repositories>
<jpa:repositories base-package="org.springframework.data.jpa.repository.sample"
entity-manager-factory-ref="entityManagerFactory-2"
transaction-manager-ref="transactionManager-2">
<repository:include-filter type="assignable" expression="org.springframework.data.jpa.repository.sample.AuditableUserRepository" />
</jpa:repositories>
How would you declare both of the above jpa:repositories configurations using java configuration and the @EnableJpaRepositories annotation?
The annotation seems to support only one set of attributes (i.e. for one jpa:repository only) and it is not possible to declare the annotation multiple times.
I created a 'minimal' multiple datasource project to help me work out how to do this. There are 7 Java classes and other config in there, so I will only post key extracts in this answer. You can get the full project from GitHub: https://github.com/gratiartis/multids-demo
The demo sets up two JPA entities:
Associated with these we will create two repositories. Thanks to the awesomeness of Spring Data, we can get ourselves some pretty full-featured repositories purely by defining interfaces which extend JpaRepository:
Now we need to ensure that each of these maps to a table in its own database.
To achieve this, we will need two separate entity managers, each of which has a different datasource. However, in a Spring Java config
@Configuration
class, we can only have one@EnableJpaRepositories
annotation and each such annotation can only reference one EntityManagerFactory. To achieve this, we create two separate@Configuration
classes: FooConfig and BarConfig.Each of these @Configuration classes will define a DataSource based on an embedded HSQL database:
Each configuration should define an EntityManagerFactory, as above, which references its own dataSource() @Bean method. It also defines a path to the @Entity beans which it manages. You need to make sure that @Entity beans for different data sources are in different packages.
At this point it's worth noting that if each of these configurations uses the default namings for key persistence beans (i.e. entityManagerFactory), then Spring will see that there are two beans with the EntityManager interface, both of which have the same name. So one will be chosen. This leads to errors such as:
This can be seen in the branch of the demo project here: https://github.com/gratiartis/multids-demo/tree/1-unnamed-entitymanager-beans
This is because in that example, Spring has wired up the beans relating to the "foodb" database, and Bar is not an entity in that database. Unfortunately the BarRepository has been wired up with the Foo entity manager.
We resolve this issue by naming all our beans in each of config class. i.e.
At this point if you were to run the tests in the project, you might see warnings such as:
This is because ... drumroll ... we do not have an EntityManagerFactory with the default name "entityManagerFactory". We have one called "fooEntityManagerFactory" and another called "barEntityManagerFactory". Spring is looking for something with a default name, so we need to instruct it to wire things up differently.
As it turns out, this is incredibly simple to do. We just need to put the correct references in the @EnableJpaRepositories annotation for each @Configuration class.
As you can see, each of these @EnableJpaRepositories annotations defines a specific named EntityManagerFactory and PlatformTransactionManager. They also specify which repositories should be wired up with those beans. In the example, I have put the repositories in database-specific packages. It is also possible to define each individual repository by name, by adding includeFilters to the annotation, but by segregating the repositories by database, I believe that things should end up more readable.
At this point you should have a working application using Spring Data repositories to manage entities in two separate databases. Feel free to grab the project from the link above and run the tests to see this happening. Hopefully this answer is useful to more folks, as I have spent a decent amount of time working out to do this as cleanly as possible with as little code as I could manage. Any ideas for improvement of the answer or demo project are welcome.
You may try put it on two
@Configuration
classes (one@EnableJpaRepositories
per@Configuration
).