Аннотация ресурса: не определяется квалификационный bean типа [javax.sql.DataSource]: ожидаемое одиночное соответствие bean, но найдено 2

Я использую конфигурацию на основе Java Spring для настройки нескольких баз данных с данными Spring. В файле конфигурации я создаю два data source для MySQL и MSSQL-Server. При попытке ввести зависимость с менеджером сущности с помощью аннотации @Resource я получаю следующее исключение:

org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [javax.sql.DataSource] is defined: expected single matching bean but found 2: mysql_datasource,secure_datasource
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1016)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:904)
at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:815)
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:743)

Ниже приведен мой код:

@Bean(name="secure_datasource")
public DataSource dataSource(){
    try{
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setJdbcUrl(environment.getProperty("sc.db.url"));
        dataSource.setDriverClass(environment.getProperty("sc.db.driver.class"));
        dataSource.setUser(environment.getProperty("sc.db.username"));
        dataSource.setPassword(environment.getProperty("sc.db.password"));
        dataSource.setIdleConnectionTestPeriod(60);
        dataSource.setMaxPoolSize(10);
        dataSource.setMaxStatements(7);
        dataSource.setMinPoolSize(1);
        return dataSource; 
    }catch(Exception ex){
        throw new RuntimeException(ex);
    }
}

.................

@Bean(name="mysql_datasource")
public DataSource dataSource(){
    try{
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setJdbcUrl(environment.getProperty("db.url"));
        dataSource.setDriverClass(environment.getProperty("db.driver.class"));
        dataSource.setUser(environment.getProperty("db.username"));
        dataSource.setPassword(environment.getProperty("db.password"));
        dataSource.setIdleConnectionTestPeriod(60);
        dataSource.setMaxPoolSize(100);
        dataSource.setMaxStatements(50);
        dataSource.setMinPoolSize(10);
        return dataSource; 
    }catch(Exception ex){
        throw new RuntimeException(ex);
    }
}

.......

@Resource(value="mysql_datasource")
@Bean(name="entity_manager_factory")
public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource){
    LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
    factoryBean.setDataSource(dataSource);
    factoryBean.setPersistenceProviderClass(HibernatePersistenceProvider.class);
    factoryBean.setPackagesToScan(environment.getProperty("package.scan"));
    factoryBean.setJpaVendorAdapter(jpaVendorAdapter);
    return factoryBean;
}

Я также пытаюсь использовать аннотацию @Qualifier, предлагая эту ссылку, но все же получаю ошибку. Используя 2 beans того же типа: javax.sql.DataSource в Spring

Ответ 1

У меня была такая же проблема, и после большой головной боли я наткнулся на этот doc, который заставил меня чувствовать себя действительно немым: (

Все, что вам нужно, это @Primary на одном из ваших DataSources и Spring -Boot больше не будет запутаться... Вот одна из моих конфигураций... Остальные в значительной степени идентичны, указывая на другие БД и без @Primary на них...

@Configuration
@EnableTransactionManagement
@EntityScan(basePackages = {"somepackage.entities"})
@EnableJpaRepositories(entityManagerFactoryRef = "emfDB1", transactionManagerRef = "tmDB1", basePackages = {"somepackage.repositories"})
class PersistenceDB1 {
    @Bean
    @Primary
    DataSource dsDB1() {
        BasicDataSource dataSource = new BasicDataSource();
        dataSource.setUrl("jdbc:mysql://someserver:3306/proativo");
        dataSource.setUsername("username");
        dataSource.setPassword("password");
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        return dataSource;
    }

    @Bean
    @Primary
    LocalContainerEntityManagerFactoryBean emfDB1() {
        LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
        entityManagerFactoryBean.setDataSource(dsDB1());
        entityManagerFactoryBean.setJpaVendorAdapter(new EclipseLinkJpaVendorAdapter());

        entityManagerFactoryBean.setPersistenceXmlLocation("classpath:META-INF/DB1-persistence.xml");

        Properties jpaProperties = new Properties();
        jpaProperties.put("eclipselink.weaving", "false");
        jpaProperties.put("eclipselink.logging.level", "SEVERE"); // SEVERE / FINEST

        entityManagerFactoryBean.setJpaProperties(jpaProperties);
        return entityManagerFactoryBean;
    }

    @Bean
    @Primary
    JpaTransactionManager tmDB1() {
        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(emfDB1().getNativeEntityManagerFactory());
        return transactionManager;
    }
}

Изменить: Забыл упомянуть: Вероятно, из-за того, как мои классы конфигурации выполнены, метод исключения некоторых классов в @EnableAutoConfiguration не работал у меня...

Ответ 2

У меня была такая же проблема, и я нашел это старое сообщение, которое объясняло это:

http://xantorohara.blogspot.ch/2013/11/spring-boot-jdbc-with-multiple.html

Похоже, что если вам нужны несколько источников данных, магия загрузки Spring заканчивается, и вам нужно взять ее вручную.

В моем случае мне пришлось модифицировать EnableAutoConfiguration, чтобы самостоятельно использовать конфигурацию источника данных и менеджера транзакций:

@EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class,DataSourceTransactionManagerAutoConfiguration.class}) 

Похоже, что волшебство загрузки Spring закончилось в тот момент, когда мне пришлось использовать несколько источников данных...

Ответ 3

В моем случае конфигурация была в xml файле, поэтому мне нужно добавить первичный = "true" в тег bean одного из определенных источников данных.