Reputation: 4530
I am using spring-boot-starter-jdbc and trying to use multiple Jdbc DataSources, everything worked when I used named Beans for my JdbcTemplate and then used @Qualifier to inject the right JdbcTemplate into my repository.
Application:
package my.app;
@SpringBootApplication
@EnableAutoConfiguration
public class Application implements CommandLineRunner {
@Autowired
private MyRepository repository;
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Override
public void run(String... args) throws Exception {
List<Stuff> stuff = repository.getStuff();
}
}
Configuration:
package my.app;
@Configuration
@ComponentScan
public class AppConfig {
@Bean(name = "datasource1")
@ConfigurationProperties(prefix = "db1.datasource")
public DataSource dataSource1() {
return DataSourceBuilder.create().build();
}
@Bean(name = "db1")
public DataSource db1(@Qualifier("datasource1" DataSource ds) {
return new JdbcTemplate(ds);
}
@Bean(name = "datasource2")
@ConfigurationProperties(prefix = "db2.datasource")
public DataSource dataSource2() {
return DataSourceBuilder.create().build();
}
@Bean(name = "db2")
public DataSource db1(@Qualifier("datasource1" DataSource ds) {
return new JdbcTemplate(ds);
}
}
Repository:
package my.app;
@Repository
public class MyRepository {
private JdbcTemplate db1;
private JdbcTemplate db2;
@Autowired
public class MyRepository(@Qualifier("db1") JdbcTemplate db1, @Qualifier("db2") JdbcTemplate db2) {
this.db1 = db1;
this.db2 = db2;
}
}
When I instantiate MyRepository everything is fine.
I did a little refactoring to try to make new classes for Db1JdbcTemplate and Db2JdbcTemplate so I could inject them without qualifiers. Unfortunately when I do this I get the following exception:
Here's what I attempted to do:
Removed named JdbcTemplate Beans from AppConfig:
package my.app;
@Configuration
@ComponentScan
public class AppConfig {
@Bean(name = "datasource1")
@ConfigurationProperties(prefix = "db1.datasource")
public DataSource dataSource1() {
return DataSourceBuilder.create().build();
}
@Bean(name = "datasource2")
@ConfigurationProperties(prefix = "db2.datasource")
public DataSource dataSource2() {
return DataSourceBuilder.create().build();
}
}
Created 2 new classes for named JdbcTemplates:
package my.app;
@Component
public class Db1JdbcTemplate extends JdbcTemplate {
@Autowired
public Db1JdbcTemplate(@Qualifier("datasource1") DataSource ds1) {
super(ds1);
}
}
package my.app;
@Component
public class Db2JdbcTemplate extends JdbcTemplate {
@Autowired
public Db2JdbcTemplate(@Qualifier("datasource2") DataSource ds2) {
super(ds2);
}
}
Modified MyRepository to use those:
package my.app;
@Repository
public class MyRepository {
private Db1JdbcTemplate db1;
private Db2JdbcTemplate db2;
@Autowired
public class MyRepository(Db1JdbcTemplate db1, Db2JdbcTemplate db2) {
this.db1 = db1;
this.db2 = db2;
}
}
So I have @Component annotations on both of those so I expected to be able to be registered and be able to be Autowired. However when I run things I get the following exception:
Error creating bean with name 'repository' defined in file [/Users/me/spring/target/classes/my/app/MyRepository.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'my.app.Db1JdbcTemplate' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
Any ideas what I need to change or how I can debug further? I can always go back to the working version but I'd prefer to be more explicit when possible so the compiler can help me out instead of runtime errors caused by a typo'd Qualifier name.
Upvotes: 0
Views: 580
Reputation: 781
If the main concern is typos, just define your names as constants (something like public static final String DATASOURCE1 = "datasource1";
) and consistently use those in your qualifier annotations instead of string literals. No need to add new classes or interfaces.
Upvotes: 0
Reputation: 692131
A more standard way of doing this and avoid typos in qualifier names is to avoid using qualifier names, and instead use Qualifier annotations:
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Qualifier
public @interface DB1 {
}
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Qualifier
public @interface DB2 {
}
and then, when defining your beans:
@Configuration
@ComponentScan
public class AppConfig {
@Bean
@ConfigurationProperties(prefix = "db1.datasource")
@DB1
public DataSource dataSource1() {
return DataSourceBuilder.create().build();
}
@Bean
@DB1
public JdbcTemplate db1(@DB1 DataSource ds) {
return new JdbcTemplate(ds);
}
@Bean
@ConfigurationProperties(prefix = "db2.datasource")
@DB2
public DataSource dataSource2() {
return DataSourceBuilder.create().build();
}
@Bean
@DB2
public JdbcTemplate db1(@DB2 DataSource ds) {
return new JdbcTemplate(ds);
}
}
And finally when injecting your beans:
public class MyRepository(@DB1 JdbcTemplate db1, @DB2 JdbcTemplate db2) {
...
}
Upvotes: 2