Spring Boot,Spring Data JPA,带有多个DataSources [英] Spring Boot, Spring Data JPA with multiple DataSources

查看:481
本文介绍了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})
公共类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);

返回em;
}

属性additionalJpaProperties(){
属性properties = new Properties();
properties.setProperty(hibernate.hbm2ddl.auto,create-drop);
properties.setProperty(hibernate.dialect,org.hibernate.dialect.H2Dialect);
properties.setProperty(hibernate.show_sql,true);

返回属性;
}

@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);

返回transactionManager;
}
}

CustomerDbConfig.java (第二个数据源)

  @Configuration 
@EnableJpaRepositories(
entityManagerFactoryRef =orderEntityManager,
transactionManagerRef =orderTransactionManager,
basePackages = {com.mm.repository.customer})
公共类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);

返回em;
}

属性additionalJpaProperties(){
属性properties = new Properties();
properties.setProperty(hibernate.hbm2ddl.auto,create-drop);
properties.setProperty(hibernate.dialect,org.hibernate.dialect.H2Dialect);
properties.setProperty(hibernate.show_sql,true);

返回属性;
}

@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);

返回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整数年龄;

....

Order.java (型号)

  @Entity 
@Table(name =order)
@Data
@EqualsAndHashCode(exclude = {id})
public class Order {

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private整数id;

@Column(name =code,nullable = false)
private整数代码;

@Column(name =quality,nullable = false)
private整数质量;

...



CustomerRepository.java

 公共接口CustomerRepository扩展JpaRepository< Customer,Integer> {

}

OrderRepository.java

  public interface OrderRepository扩展了JpaRepository< Order,Integer> {

}

最后, 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);
返回reg;
}
}

在启动期间,以下例外被抛出:

  -10-10 15:45:24.757 ERROR 1549 --- [main] osboot.SpringApplication:应用程序启动失败

org.springframework.beans.factory.UnsatisfiedDependencyException:在类路径资源[com / mm / boot / multidb / CustomerConfig.class]中定义名称为'customerTransactionManager'的bean时出错:不满意的依赖项通过类型为[javax.persistence.EntityManagerFactory]的索引0的构造函数参数表示::没有定义类型为[javax.persistence.EntityManagerFactory]的限定bean:期望的单个匹配bean但找到2:customerEntityManager,orderEntityManager;嵌套异常是org.springframework.beans.factory.NoUniqueBeanDefinitionException:没有定义类型为[javax.persistence.EntityManagerFactory]的限定bean:期望的单个匹配bean但在org.springframework.beans中找到2:customerEntityManager,orderEntityManager
。 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.springfram ework.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:48 2)
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)
引起:org.springframework.beans.factory.NoUniqueBeanDefinitionException :没有定义[javax.persistence.EntityManagerFactory]类型的限定bean:期望的单个匹配bean但是找到2:customerEntityManager,orderEntityManager
org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java: 974)org.springframework.beans.factory.support.Defa上的
ultListableBeanFactory.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个常见帧

线程main中的异常org.springframework.beans.factory.UnsatisfiedDependencyException:使用名称创建bean时出错类路径资源[com / mm / boot / multidb / CustomerConfig.class]中定义的'customerTransactionManager':通过构造函数参数表达的不满意的依赖关系,类型为[javax.persistence.EntityManagerFactory]的索引为0 ::没有类型[javax的限定bean]。 persistence.EntityManagerFactory]定义:期望的单个匹配bean但找到2:customerEntityManager,orderEntityManager;嵌套异常是org.springframework.beans.factory.NoUniqueBeanDefinitionException:没有定义类型为[javax.persistence.EntityManagerFactory]的限定bean:期望的单个匹配bean但在org.springframework.beans中找到2:customerEntityManager,orderEntityManager
。 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.springfram ework.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:48 2)
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)
引起:org.springframework.beans.factory.NoUniqueBeanDefinitionException :没有定义[javax.persistence.EntityManagerFactory]类型的限定bean:期望的单个匹配bean但是找到2:customerEntityManager,orderEntityManager
org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java: 974)org.springframework.beans.factory.support.Defa上的
ultListableBeanFactory.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更多

完整代码可以在GitHub上找到样本( https://github.com/ tonym2105 / samples / tree / master / boot-multidb-sample



提前感谢您的帮助。

解决方案

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



基本上放application.properties上有多个dataSource配置信息,并自动生成默认设置(dataSource和entityManagerFactory) r @EnableAutoConfiguration的第一个dataSource。但对于下一个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'。 / p>

由于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); //< - 赋予'default'作为名称
emf.afterPropertiesSet();
返回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})
公共类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);
返回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); //< - 实体包
emf.setPersistenceUnitName(anotherPersistenceUnit);
emf.setJpaProperties(properties);
emf.afterPropertiesSet();
返回emf.getObject();
}

@Bean(name =anotherTransactionManager)
public PlatformTransactionManager transactionManager(){
return new JpaTransactionManager(entityManagerFactory());
}
}

application.properties

  #database配置
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.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)
公共类DefaultDataSourceServiceImpl实现DefaultDataSourceService
{

//

}

其他数据源的服务

  @Service(anotherService)
@Transactional(anotherTransactionManager)
公共类AnotherDataSourceServiceImpl实现AnotherDataSourceService
{

//

}


I'm trying to connect each @Repositories to different DataSource(s) with Spring Boot and Spring Data JPA. I used the following, http://xantorohara.blogspot.com/2013/11/spring-boot-jdbc-with-multiple.html, as a referrence. Here is the code I am using in an attempt to implement a similar solution using Spring Data JPA.

CustomerDbConfig.java(First data source connection)

@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 (Second data source)

@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 (model)

@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 (model)

@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<Customer, Integer>{

}

OrderRepository.java

public interface OrderRepository extends JpaRepository<Order, Integer> {

}

Finally, 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;
        }
}

During start the following exceptions are thrown:

-10-10 15:45:24.757 ERROR 1549 --- [           main] o.s.boot.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

Full code for the sample can be found on GitHub (https://github.com/tonym2105/samples/tree/master/boot-multidb-sample)

Thank you in advance for the help.

解决方案

There is another way to have multiple dataSources by using @EnableAutoConfiguration and application.properties.

Basically put multiple dataSource configuration info on application.properties and generate default setup (dataSource and entityManagerFactory) automatically for first dataSource by @EnableAutoConfiguration. But for next dataSource, create dataSource, entityManagerFactory and transactionManager all manually by the info from property file.

Below is my example to setup two dataSources. First dataSource is setup by @EnableAutoConfiguration which can be assigned only for one configuration, not multiple. And that will generate 'transactionManager' by DataSourceTransactionManager, that looks default transactionManager generated by the annotation. However I have seen the transaction not beginning issue on the thread from scheduled thread pool only for the default DataSourceTransactionManager and also when there are multiple transaction managers. So I create transactionManager manually by JpaTransactionManager also for the first dataSource with assigning 'transactionManager' bean name and default entityManagerFactory. That JpaTransactionManager for first dataSource surely resolves the weird transaction issue on the thread from ScheduledThreadPool.

Update for Spring Boot 1.3.0.RELEASE

I found my previous configuration with @EnableAutoConfiguration for default dataSource has issue on finding entityManagerFactory with Spring Boot 1.3 version. Maybe default entityManagerFactory is not generated by @EnableAutoConfiguration, once after I introduce my own transactionManager. So now I create entityManagerFactory by myself. So I don't need to use @EntityScan. So it looks I'm getting more and more out of the setup by @EnableAutoConfiguration.

Second dataSource is setup without @EnableAutoConfiguration and create 'anotherTransactionManager' by manual way.

Since there are multiple transactionManager extends from PlatformTransactionManager, we should specify which transactionManager to use on each @Transactional annotation

Default Repository Config

@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;
    }
}

Another Repository Config

@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

Choose proper transactionManager for @Transactional annotation

Service for first datasource

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

   //

}

Service for another datasource

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

   //

}

这篇关于Spring Boot,Spring Data JPA,带有多个DataSources的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆