Spring Boot,Spring Data JPA,带有多个DataSources

我正在尝试使用Spring Boot和Spring Data JPA将每个@Repositories连接到不同的DataSource。 我使用以下内容http://xantorohara.blogspot.com/2013/11/spring-boot-jdbc-with-multiple.html作为参考。 以下是我试图使用Spring Data JPA实现类似解决方案时使用的代码。

CustomerDbConfig.java (第一个数据源连接)

@Configuration @EnableJpaRepositories( entityManagerFactoryRef = "orderEntityManager", transactionManagerRef = "orderTransactionManager", basePackages = {"com.mm.repository.customer"}) public class CustomerDbConfig { @Bean(name = "customerEntityManager") public LocalContainerEntityManagerFactoryBean entityManagerFactory(){ LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean(); em.setDataSource(dataSource()); em.setPackagesToScan(new String[] {"com.mm.domain.customer"}); JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter(); em.setJpaVendorAdapter(vendorAdapter); em.setJpaProperties(additionalJpaProperties()); em.setPersistenceUnitName("customerPersistence"); em.setPackagesToScan("com.mm.domain.customer"); return em; } Properties additionalJpaProperties(){ Properties properties = new Properties(); properties.setProperty("hibernate.hbm2ddl.auto", "create-drop"); properties.setProperty("hibernate.dialect", "org.hibernate.dialect.H2Dialect"); properties.setProperty("hibernate.show_sql", "true"); return properties; } @Bean public DataSource dataSource(){ return DataSourceBuilder.create() .url("jdbc:h2:mem:customer:H2") .driverClassName("org.h2.Driver") .username("sa") .password("") .build(); } @Bean(name = "customerTransactionManager") public PlatformTransactionManager transactionManager(EntityManagerFactory emf){ JpaTransactionManager transactionManager = new JpaTransactionManager(); transactionManager.setEntityManagerFactory(emf); return transactionManager; } } 

CustomerDbConfig.java (第二个数据源)

 @Configuration @EnableJpaRepositories( entityManagerFactoryRef = "orderEntityManager", transactionManagerRef = "orderTransactionManager", basePackages = {"com.mm.repository.customer"}) public class CustomerDbConfig { @Bean(name = "customerEntityManager") public LocalContainerEntityManagerFactoryBean entityManagerFactory(){ LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean(); em.setDataSource(dataSource()); em.setPackagesToScan(new String[] {"com.mm.domain.customer"}); JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter(); em.setJpaVendorAdapter(vendorAdapter); em.setJpaProperties(additionalJpaProperties()); em.setPersistenceUnitName("customerPersistence"); em.setPackagesToScan("com.mm.domain.customer"); return em; } Properties additionalJpaProperties(){ Properties properties = new Properties(); properties.setProperty("hibernate.hbm2ddl.auto", "create-drop"); properties.setProperty("hibernate.dialect", "org.hibernate.dialect.H2Dialect"); properties.setProperty("hibernate.show_sql", "true"); return properties; } @Bean public DataSource dataSource(){ return DataSourceBuilder.create() .url("jdbc:h2:mem:customer:H2") .driverClassName("org.h2.Driver") .username("sa") .password("") .build(); } @Bean(name = "customerTransactionManager") public PlatformTransactionManager transactionManager(EntityManagerFactory emf){ JpaTransactionManager transactionManager = new JpaTransactionManager(); transactionManager.setEntityManagerFactory(emf); return transactionManager; } } 

Customer.java (型号)

 @Entity @Table(name = "customer") @Data @EqualsAndHashCode(exclude = {"id"}) public class Customer { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Integer id; @Column(name = "name", nullable = false) private String name; @Column(name = "age", nullable = false) private Integer age; .... 

Order.java (型号)

 @Entity @Table(name = "order") @Data @EqualsAndHashCode(exclude = {"id"}) public class Order { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Integer id; @Column(name = "code", nullable = false) private Integer code; @Column(name = "quality", nullable = false) private Integer quality; 

CustomerRepository.java

 public interface CustomerRepository extends JpaRepository{ } 

OrderRepository.java

 public interface OrderRepository extends JpaRepository { } 

最后, Application.java

 @Configuration @ComponentScan @EnableAutoConfiguration public class Application extends SpringApplication{ public static void main(String[] args) { SpringApplication.run(Application.class, args); } @Bean public ServletRegistrationBean h2Console() { ServletRegistrationBean reg = new ServletRegistrationBean(new WebServlet(), "/console/*"); reg.setLoadOnStartup(1); return reg; } } 

在启动期间,抛出以下exception

 -10-10 15:45:24.757 ERROR 1549 --- [ main] osboot.SpringApplication : Application startup failed org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'customerTransactionManager' defined in class path resource [com/mm/boot/multidb/CustomerConfig.class]: Unsatisfied dependency expressed through constructor argument with index 0 of type [javax.persistence.EntityManagerFactory]: : No qualifying bean of type [javax.persistence.EntityManagerFactory] is defined: expected single matching bean but found 2: customerEntityManager,orderEntityManager; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [javax.persistence.EntityManagerFactory] is defined: expected single matching bean but found 2: customerEntityManager,orderEntityManager at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:747) at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:462) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1095) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:990) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:504) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:475) at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:302) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:298) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193) at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:706) at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:762) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:482) at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:109) at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:691) at org.springframework.boot.SpringApplication.run(SpringApplication.java:320) at org.springframework.boot.SpringApplication.run(SpringApplication.java:952) at org.springframework.boot.SpringApplication.run(SpringApplication.java:941) at com.mm.boot.multidb.Application.main(Application.java:17) Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [javax.persistence.EntityManagerFactory] is defined: expected single matching bean but found 2: customerEntityManager,orderEntityManager at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:974) at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:862) at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:811) at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:739) ... 18 common frames omitted Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'customerTransactionManager' defined in class path resource [com/mm/boot/multidb/CustomerConfig.class]: Unsatisfied dependency expressed through constructor argument with index 0 of type [javax.persistence.EntityManagerFactory]: : No qualifying bean of type [javax.persistence.EntityManagerFactory] is defined: expected single matching bean but found 2: customerEntityManager,orderEntityManager; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [javax.persistence.EntityManagerFactory] is defined: expected single matching bean but found 2: customerEntityManager,orderEntityManager at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:747) at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:462) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1095) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:990) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:504) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:475) at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:302) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:298) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193) at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:706) at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:762) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:482) at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:109) at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:691) at org.springframework.boot.SpringApplication.run(SpringApplication.java:320) at org.springframework.boot.SpringApplication.run(SpringApplication.java:952) at org.springframework.boot.SpringApplication.run(SpringApplication.java:941) at com.mm.boot.multidb.Application.main(Application.java:17) Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [javax.persistence.EntityManagerFactory] is defined: expected single matching bean but found 2: customerEntityManager,orderEntityManager at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:974) at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:862) at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:811) at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:739) ... 18 more 

可以在GitHub上找到该示例的完整代码( https://github.com/tonym2105/samples/tree/master/boot-multidb-sample )

提前感谢您的帮助。

通过使用@EnableAutoConfiguration和application.properties,还有另一种方法可以拥有多个dataSource。

基本上在application.properties上放置多个dataSource配置信息,并通过@EnableAutoConfiguration自动为第一个dataSource生成默认设置(dataSource和entityManagerFactory)。 但对于下一个dataSource,请通过属性文件中的信息手动创建dataSource,entityManagerFactory和transactionManager。

下面是我设置两个dataSource的示例。 第一个dataSource由@EnableAutoConfiguration设置,只能为一个配置而不是多个配置。 这将由DataSourceTransactionManager生成“transactionManager”,它看起来是由注释生成的默认transactionManager。 但是,我已经看到事务没有从调度线程池中的线程开始问题仅针对默认的DataSourceTransactionManager以及有多个事务管理器时。 所以我也通过JpaTransactionManager为第一个dataSource手动创建transactionManager,并指定’transactionManager’bean名称和默认的entityManagerFactory。 第一个dataSource的JpaTransactionManager肯定会解决来自ScheduledThreadPool的线程上的奇怪事务问题。

Spring Boot 1.3.0.RELEASE的更新

我发现我以前使用@EnableAutoConfiguration配置的默认dataSource在查找带有Spring Boot 1.3版本的entityManagerFactory时遇到了问题。 在我介绍自己的transactionManager之后,可能默认的entityManagerFactory不是由@EnableAutoConfiguration生成的。 所以现在我自己创建entityManagerFactory。 所以我不需要使用@EntityScan。 因此看起来我越来越多地通过@EnableAutoConfiguration进行设置了。

第二个dataSource在没有@EnableAutoConfiguration的情况下设置,并通过手动方式创建“anotherTransactionManager”。

由于从PlatformTransactionManager扩展了多个transactionManager,我们应该指定在每个@Transactional注释上使用哪个transactionManager

默认存储库配置

 @Configuration @EnableTransactionManagement @EnableAutoConfiguration @EnableJpaRepositories( entityManagerFactoryRef = "entityManagerFactory", transactionManagerRef = "transactionManager", basePackages = {"com.mysource.repository"}) public class RepositoryConfig { @Autowired JpaVendorAdapter jpaVendorAdapter; @Autowired DataSource dataSource; @Bean(name = "entityManager") public EntityManager entityManager() { return entityManagerFactory().createEntityManager(); } @Primary @Bean(name = "entityManagerFactory") public EntityManagerFactory entityManagerFactory() { LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean(); emf.setDataSource(dataSource); emf.setJpaVendorAdapter(jpaVendorAdapter); emf.setPackagesToScan("com.mysource.model"); emf.setPersistenceUnitName("default"); // <- giving 'default' as name emf.afterPropertiesSet(); return emf.getObject(); } @Bean(name = "transactionManager") public PlatformTransactionManager transactionManager() { JpaTransactionManager tm = new JpaTransactionManager(); tm.setEntityManagerFactory(entityManagerFactory()); return tm; } } 

另一个存储库配置

 @Configuration @EnableTransactionManagement @EnableJpaRepositories( entityManagerFactoryRef = "anotherEntityManagerFactory", transactionManagerRef = "anotherTransactionManager", basePackages = {"com.mysource.anothersource.repository"}) public class AnotherRepositoryConfig { @Autowired JpaVendorAdapter jpaVendorAdapter; @Value("${another.datasource.url}") private String databaseUrl; @Value("${another.datasource.username}") private String username; @Value("${another.datasource.password}") private String password; @Value("${another.dataource.driverClassName}") private String driverClassName; @Value("${another.datasource.hibernate.dialect}") private String dialect; public DataSource dataSource() { DriverManagerDataSource dataSource = new DriverManagerDataSource(databaseUrl, username, password); dataSource.setDriverClassName(driverClassName); return dataSource; } @Bean(name = "anotherEntityManager") public EntityManager entityManager() { return entityManagerFactory().createEntityManager(); } @Bean(name = "anotherEntityManagerFactory") public EntityManagerFactory entityManagerFactory() { Properties properties = new Properties(); properties.setProperty("hibernate.dialect", dialect); LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean(); emf.setDataSource(dataSource()); emf.setJpaVendorAdapter(jpaVendorAdapter); emf.setPackagesToScan("com.mysource.anothersource.model"); // <- package for entities emf.setPersistenceUnitName("anotherPersistenceUnit"); emf.setJpaProperties(properties); emf.afterPropertiesSet(); return emf.getObject(); } @Bean(name = "anotherTransactionManager") public PlatformTransactionManager transactionManager() { return new JpaTransactionManager(entityManagerFactory()); } } 

application.properties

 # database configuration spring.datasource.url=jdbc:h2:file:~/main-source;AUTO_SERVER=TRUE spring.datasource.username=sa spring.datasource.password= spring.datasource.driver-class-name=org.h2.Driver spring.datasource.continueOnError=true spring.datasource.initialize=false # another database configuration another.datasource.url=jdbc:sqlserver://localhost:1433;DatabaseName=another; another.datasource.username=username another.datasource.password= another.datasource.hibernate.dialect=org.hibernate.dialect.SQLServer2008Dialect another.datasource.driverClassName=com.microsoft.sqlserver.jdbc.SQLServerDriver 

为@Transactional注释选择适当的transactionManager

为第一个数据源提供服务

 @Service("mainService") @Transactional("transactionManager") public class DefaultDataSourceServiceImpl implements DefaultDataSourceService { // } 

为另一个数据源提供服务

 @Service("anotherService") @Transactional("anotherTransactionManager") public class AnotherDataSourceServiceImpl implements AnotherDataSourceService { // } 

这是我的解决方案。 基于spring-boot.1.2.5.RELEASE。

application.properties

 first.datasource.driver-class-name=com.mysql.jdbc.Driver first.datasource.url=jdbc:mysql://127.0.0.1:3306/test first.datasource.username= first.datasource.password= first.datasource.validation-query=select 1 second.datasource.driver-class-name=com.mysql.jdbc.Driver second.datasource.url=jdbc:mysql://127.0.0.1:3306/test2 second.datasource.username= second.datasource.password= second.datasource.validation-query=select 1 

DataSourceConfig.java

 @Configuration public class DataSourceConfig { @Bean @Primary @ConfigurationProperties(prefix="first.datasource") public DataSource firstDataSource() { DataSource ds = DataSourceBuilder.create().build(); return ds; } @Bean @ConfigurationProperties(prefix="second.datasource") public DataSource secondDataSource() { DataSource ds = DataSourceBuilder.create().build(); return ds; } } 

我检查了你在GitHub上提供的源代码。 配置中存在多个错误/拼写错误。

在CustomerDbConfig / OrderDbConfig中,您应该引用customerEntityManager,并且包应该指向现有的包:

 @Configuration @EnableJpaRepositories( entityManagerFactoryRef = "customerEntityManager", transactionManagerRef = "customerTransactionManager", basePackages = {"com.mm.boot.multidb.repository.customer"}) public class CustomerDbConfig { 

要在customerEntityManager和orderEntityManager中扫描的包都没有指向正确的包:

 em.setPackagesToScan("com.mm.boot.multidb.model.customer"); 

注入适当的EntityManagerFactory也不起作用。 它应该是:

 @Bean(name = "customerTransactionManager") public PlatformTransactionManager transactionManager(EntityManagerFactory customerEntityManager){ } 

以上是导致问题和例外。 在@Bean方法中提供名称时,您确定可以注入适当的EMF。

我做的最后一件事是禁用自动配置JpaRepositories:

 @EnableAutoConfiguration(exclude = JpaRepositoriesAutoConfiguration.class) 

通过所有修复程序,应用程序就像您预期的那样启动!

感谢Steve Park和Rafal Borowiec的答案我得到了我的代码,但是,我有一个问题:DriverManagerDataSource是一个“简单”的实现,并没有给你一个ConnectionPool (查看http://docs.spring.io/ spring / docs / current / javadoc-api / org / springframework / jdbc / datasource / DriverManagerDataSource.html )。

因此,我替换了将secondDBDataSource返回的函数。

 public DataSource DataSource() { // use DataSourceBuilder and NOT DriverManagerDataSource // as this would NOT give you ConnectionPool DataSourceBuilder dataSourceBuilder = DataSourceBuilder.create(); dataSourceBuilder.url(databaseUrl); dataSourceBuilder.username(username); dataSourceBuilder.password(password); dataSourceBuilder.driverClassName(driverClassName); return dataSourceBuilder.build(); } 

此外,如果您不需要EntityManager ,则可以删除entityManager()@Bean注释。

另外,您可能希望删除配置类的basePackages批注:使用factoryBean.setPackagesToScan()调用来维护它是足够的。

不知道为什么,但它的确有效。 两个配置相同,只需将xxx更改为您的名字即可。

 @Configuration @EnableTransactionManagement @EnableJpaRepositories( entityManagerFactoryRef = "xxxEntityManager", transactionManagerRef = "xxxTransactionManager", basePackages = {"aaa.xxx"}) public class RepositoryConfig { @Autowired private Environment env; @Bean @Primary @ConfigurationProperties(prefix="datasource.xxx") public DataSource xxxDataSource() { return DataSourceBuilder.create().build(); } @Bean public LocalContainerEntityManagerFactoryBean xxxEntityManager() { LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean(); em.setDataSource(xxxDataSource()); em.setPackagesToScan(new String[] {"aaa.xxx"}); HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter(); em.setJpaVendorAdapter(vendorAdapter); HashMap properties = new HashMap(); properties.put("hibernate.show_sql", env.getProperty("hibernate.show_sql")); properties.put("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto")); properties.put("hibernate.dialect", env.getProperty("hibernate.dialect")); em.setJpaPropertyMap(properties); return em; } @Bean(name = "xxxTransactionManager") public PlatformTransactionManager xxxTransactionManager() { JpaTransactionManager tm = new JpaTransactionManager(); tm.setEntityManagerFactory(xxxEntityManager().getObject()); return tm; } 

}