4

I have a SpringBoot app. that has to access to different datasources to export data from 1 to another (1 local datasource and another remote datasource)

This is how my persistenceConfig looks like

public class PersistenceConfig { @Bean public JdbcTemplate localJdbcTemplate() { return new JdbcTemplate(localDataSource()); } @Bean public JdbcTemplate remoteJdbcTemplate() { return new JdbcTemplate(remoteDataSource()); } @Bean public DataSource localDataSource(){ HikariConfig config = new HikariConfig(); config.setMaximumPoolSize(getLocalDbPoolSize()); config.setMinimumIdle(5); config.setDriverClassName(getLocalDbDriverClassName()); config.setJdbcUrl(getLocalDbJdbcUrl()); config.addDataSourceProperty("user", getLocalDbUser()); config.addDataSourceProperty("password", getLocalDbPwd()); return new HikariDataSource(config); } @Bean public DataSource remoteDataSource(){ HikariConfig config = new HikariConfig(); config.setMaximumPoolSize(getRemoteDbPoolSize()); config.setMinimumIdle(5); config.setDriverClassName(getRemoteDbDriverClassName()); config.setJdbcUrl(getRemoteDbJdbcUrl()); config.addDataSourceProperty("user", getRemoteDbUser()); config.addDataSourceProperty("password", getRemoteDbPwd()); return new HikariDataSource(config); } } 

But when I init my app I got this error:

Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'javax.sql.DataSource' available: expected single matching bean but found 2: localDataSource,remoteDataSource 

I also tried to user qualified beans, as follows:

@Bean(name = "localJdbcTemplate") public JdbcTemplate localJdbcTemplate() { return new JdbcTemplate(localDataSource()); } @Bean(name = "remoteJdbcTemplate") public JdbcTemplate remoteJdbcTemplate() { return new JdbcTemplate(remoteDataSource()); } @Bean(name = "localDataSource") public DataSource localDataSource(){ HikariConfig config = new HikariConfig(); config.setMaximumPoolSize(getLocalDbPoolSize()); config.setMinimumIdle(5); config.setDriverClassName(getLocalDbDriverClassName()); config.setJdbcUrl(getLocalDbJdbcUrl()); config.addDataSourceProperty("user", getLocalDbUser()); config.addDataSourceProperty("password", getLocalDbPwd()); return new HikariDataSource(config); } @Bean(name = "remoteDataSource") public DataSource remoteDataSource(){ HikariConfig config = new HikariConfig(); config.setMaximumPoolSize(getRemoteDbPoolSize()); config.setMinimumIdle(5); config.setDriverClassName(getRemoteDbDriverClassName()); config.setJdbcUrl(getRemoteDbJdbcUrl()); config.addDataSourceProperty("user", getRemoteDbUser()); config.addDataSourceProperty("password", getRemoteDbPwd()); return new HikariDataSource(config); } 

but then I got this other error:

A component required a bean of type 'org.springframework.transaction.PlatformTransactionManager' that could not be found. - Bean method 'transactionManager' not loaded because @ConditionalOnSingleCandidate (types: javax.sql.DataSource; SearchStrategy: all) did not find a primary bean from beans 'remoteDataSource', 'localDataSource' 

I also tried

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

but then I got

A component required a bean of type 'org.springframework.transaction.PlatformTransactionManager' that could not be found. 

2 Answers 2

2
+50

You can use bean names to qualify them:

@Bean(name = "localDataSource") public DataSource localDataSource() { ... } @Bean(name = "remoteDataSource") public DataSource remoteDataSource() { ... } 

Please note: You have to do the same for your JdbcTemplate beans - just give them a name and it will work. See the Spring JavaDoc for more Information: Bean

@Bean(name = "localJdbcTemplate") public JdbcTemplate localJdbcTemplate() { return new JdbcTemplate(localDataSource()); } 

When you use your JdbcTemplate beans within your export service implementation via autowiring (@Autowired), you need to use @Qualifier to qualify them:

@Autowired @Qualifier("localJdbcTemplate") private JdbcTemplate jdbcTemplate; @Autowired @Qualifier("remoteJdbcTemplate") private JdbcTemplate jdbcTemplate; 
Sign up to request clarification or add additional context in comments.

Comments

1

Bean gets its name from the method name, providing name attribute just makes it explicit (keeping the name the same as the method name). Overall suggestion about @Bean(name="...") and @Qualifier didn't fix the error for me.

I set up sample project with two embedded databases and got the same error as the aothor. Spring suggestion was to annotate one of the DataSource beans as @Primary and, in fact, this fixes the error. Usually it happens, when some other application parts want to see only one or one primary DataSource, if several present.

What seems to be a better solution is to disable not needed autoconfiguration beans keeping rest of the code as it is:

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

or:

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

depending on which annotations are in use.

If author doesn't use any JPA provider and operates directly with JdbcTemplate it may be a suitable solution.

1 Comment

This was the solution in my case. The accepted answer did not work for me.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.