Spring Data repositories with multiple databases

来源:互联网 发布:怎样做一个软件 编辑:程序博客网 时间:2024/05/22 06:42

有人遇到了这样的需求,参考了这样一篇博客的文章。转自:http://scattercode.co.uk/2013/11/18/spring-data-multiple-databases/

参考代码地址:https://github.com/gratiartis/multids-demo

The Spring Data project keeps making it easier to do database access in Spring applications, and one of the neatest  improvements of recent times is that by defining an interface which extends JpaRepository and referencing a JPA entity, an implementation will automatically be injected with all the usual CRUD methods: findAll(), findOne(id), save(entity), delete(id), etc.

Recently I was working on a project, where I had taken full advantage of this, and for which I needed to add domain objects from an additional database. Unfortunately, as soon as I added references to entities in a different database I started experiencing troubles. For instance:

Not an managed type: class com.sctrcd.multidsdemo.domain.bar.Bar

… which was being caused by my repository being injected with the entityManager and transactionManager for the other database. After naming my beans to ensure that I would not be referencing the wrong one, I started seeing:

No bean named 'entityManagerFactory' is defined.

… because the repository implementation defaults to a by-name search for a bean called “entityManagerFactory”.

I was struggling to find any documentation of how to do it right, so to help me work through the steps of such a configuration, I created a minimal demo project at GitHub, containing two entities with those traditional names: Foo and Bar.

https://github.com/gratiartis/multids-demo

Here I shall explain the configuration that I ended up with in the hope that readers might understand that it can be done, and that it’s actually quite easy and requires very little code, if you know how!

First of all, we set up two JPA entities, Foo and Bar:

@Entity public class Foo { /* Constructors, fields and accessors/mutators */ }@Entity public class Bar { /* Constructors, fields and accessors/mutators */ }

Associated with these we create two repositories: FooRepository and BarRepository. Thanks to the awesomeness of Spring Data, we can get ourselves some pretty full-featured repositories purely by defining interfaces which extend JpaRepository:

public interface FooRepository extends JpaRepository<Foo, Long> {}public interface BarRepository extends JpaRepository<Bar, Long> {}

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 defines a DataSource based on an embedded HSQL database. The following is the BarConfig. FooConfig is identical except for some different names and package paths.

123456789101112131415161718192021222324252627282930313233343536373839404142434445
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
entityManagerFactoryRef = "barEntityManagerFactory",
transactionManagerRef = "barTransactionManager",
basePackages = { "com.sctrcd.multidsdemo.integration.repositories.bar" })
public class BarConfig {
 
@Autowired
JpaVendorAdapter jpaVendorAdapter;
 
/**
* Primary because if we have activated embedded databases, we do not want
* the application to connect to an external database.
*/
@Bean(name = "barDataSource")
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setName("bardb")
.setType(EmbeddedDatabaseType.HSQL)
.build();
}
 
@Bean(name = "barEntityManager")
public EntityManager entityManager() {
return entityManagerFactory().createEntityManager();
}
 
@Bean(name = "barEntityManagerFactory")
public EntityManagerFactory entityManagerFactory() {
LocalContainerEntityManagerFactoryBean lef = new LocalContainerEntityManagerFactoryBean();
lef.setDataSource(dataSource());
lef.setJpaVendorAdapter(jpaVendorAdapter);
lef.setPackagesToScan("com.sctrcd.multidsdemo.domain.bar");
lef.setPersistenceUnitName("barPersistenceUnit");
lef.afterPropertiesSet();
return lef.getObject();
}
 
@Bean(name = "barTransactionManager")
public PlatformTransactionManager transactionManager() {
return new JpaTransactionManager(entityManagerFactory());
}
 
}
view rawBarConfig.java hosted with ❤ by GitHub

Each configuration should define a DataSource, EntityManager, EntityManagerFactory and PlatformTransactionManager. You need to make sure that @Entity beans for different data sources are in different packages. We then need to put the correct references in the @EnableJpaRepositories annotation for each @Configuration class.

@Configuration@EnableTransactionManagement@EnableJpaRepositories(        entityManagerFactoryRef = "barEntityManagerFactory",         transactionManagerRef = "barTransactionManager",        basePackages = {"com.sctrcd.multidsdemo.integration.repositories.bar"})public class BarConfig {    // ...}@Configuration@EnableTransactionManagement@EnableJpaRepositories(        entityManagerFactoryRef = "barEntityManagerFactory",         transactionManagerRef = "barTransactionManager",        basePackages = { "com.sctrcd.multidsdemo.integration.repositories.bar" })public class BarConfig {    // ...}

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. And please do let me know if you can spot any good opportunities for improvement.


0 0
原创粉丝点击