Question

I am developing a Spring Boot based application in which I would like to create 2 beans: One will point to 'Oracle' database; the other will point to Hive. I've declared them as follows:

public @Bean
BoneCPDataSource metadataDataSource() {
    BoneCPDataSource boneCPDataSource = new BoneCPDataSource();
    boneCPDataSource.setDriverClass(getDriver());
    boneCPDataSource.setJdbcUrl(getJdbcUrl());
    boneCPDataSource.setUser(getUser());
    boneCPDataSource.setPassword(getPassword());
    boneCPDataSource.setMaxConnectionsPerPartition(5);
    boneCPDataSource.setPartitionCount(5);
    return boneCPDataSource;
}

public @Bean
BasicDataSource hiveDataSource() {
    BasicDataSource basicDataSource = new BasicDataSource();

    // Note: In a separate command window, use port forwarding like this:
    //
    // ssh -L 127.0.0.1:9996:<server>:<port> -l <userid> <server>
    //
    // and then login as the generic user.

    basicDataSource.setDriverClassName("org.apache.hadoop.hive.jdbc.HiveDriver");
    basicDataSource.setUrl("jdbc:hive://127.0.0.1:9996:10000/mytable");
    return basicDataSource;
}

Problem is at the startup I am getting this:

Exception in thread "main"
org.springframework.beans.factory.BeanCreationException: Error
creating bean with name
'org.springframework.boot.actuate.autoconfigure.EndpointAutoConfiguration':
Injection of autowired dependencies failed; nested exception is
org.springframework.beans.factory.BeanCreationException: Could not
autowire field: private javax.sql.DataSource
org.springframework.boot.actuate.autoconfigure.EndpointAutoConfiguration.dataSource;
nested exception is
org.springframework.beans.factory.NoUniqueBeanDefinitionException: No
qualifying bean of type [javax.sql.DataSource] is defined: expected
single matching bean but found 2: metadataDataSource,hiveDataSource

Mainly because both of them inherit from javax.sql.DataSource. What's the best way to fix this?


EDIT:

Now I've declared them as follows:

public @Bean (name="metadataDataSource")
BoneCPDataSource metadataDataSource() {
    BoneCPDataSource boneCPDataSource = new BoneCPDataSource();
    boneCPDataSource.setDriverClass(getDriver());
    boneCPDataSource.setJdbcUrl(getJdbcUrl());
    boneCPDataSource.setUser(getUser());
    boneCPDataSource.setPassword(getPassword());
    boneCPDataSource.setMaxConnectionsPerPartition(5);
    boneCPDataSource.setPartitionCount(5);
    return boneCPDataSource;
}

public @Bean (name="hiveDataSource")
BasicDataSource hiveDataSource() {
    BasicDataSource basicDataSource = new BasicDataSource();

    // Note: In a separate command window, use port forwarding like this:
    //
    // ssh -L 127.0.0.1:9996:<server>:<port> -l <userid> <server>
    //
    // and then login as the generic user.

    basicDataSource.setDriverClassName("org.apache.hadoop.hive.jdbc.HiveDriver");
    basicDataSource.setUrl("jdbc:hive://127.0.0.1:9996:10000/mytable");
    return basicDataSource;
}

And got this exception:

Exception in thread "main"
org.springframework.beans.factory.BeanCreationException: Error
creating bean with name
'org.springframework.boot.actuate.autoconfigure.EndpointAutoConfiguration':
Injection of autowired dependencies failed; nested exception is
org.springframework.beans.factory.BeanCreationException: Could not
autowire field: private javax.sql.DataSource
org.springframework.boot.actuate.autoconfigure.EndpointAutoConfiguration.dataSource;
nested exception is
org.springframework.beans.factory.NoUniqueBeanDefinitionException: No
qualifying bean of type [javax.sql.DataSource] is defined: expected
single matching bean but found 2: metadataDataSource,hiveDataSource

The other classes are referring to these beans as follows:

public class MetadataProcessorImpl implements MetadataProcessor {

@Autowired
@Qualifier("metadataDataSource")
BoneCPDataSource metadataDataSource;

@Controller public class HiveController {

@Autowired
@Qualifier("hiveDataSource")
BasicDataSource hiveDataSource;
Était-ce utile?

La solution 2

Give different names to your beans when using @Bean:

@Bean(name="bonecpDS")
public BoneCPDataSource metadataDataSource() {
    //...
}

@Bean(name="hiveDS")
public BasicDataSource hiveDataSource() {
    //...
}

Then, when injecting the bean, use @Qualifier and specify the name of the bean:

@Component
public class FooComponent {
    @Autowired
    @Qualifier("bonecpDS")
    DataSource boneCPDataSource;
}

Autres conseils

Set one of the beam as @Primary as described in the section 67.2 Configure Two DataSources

http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#howto-two-datasources

If you want to use two data sources at the same time and they are not primary and secondary, you should disable DataSourceAutoConfiguration on your application annotated by @SpringBootApplication(excludes = {DataSourceAutoConfiguration.class}).

Since the DataSourceAutoConfiguration will init the DataSourceInitializer class. The init method in DataSourceInitializer class needs to get DataSource. When there is more than one DataSource, the system gets confused by getting which DataSource.

@SpringBootApplication(excludes = {DataSourceAutoConfiguration.class}) means that system won't load the DataSourceAutoConfiguration.class when run the application.

I faced similar issue. Added @SpringBootApplication(exclude = {DataSourceAutoConfiguration.class,DataSourceTransactionManagerAutoConfiguration.class})

and added manual configuration. It worked!

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top