Spring boot @Qualifier doesn't work with datasources
Solution 1
Declare one of your DataSource
as @Primary
.
Also you have 2 beans of same type - LocalContainerEntityManagerFactoryBean
, declare one of them @Primary
as well, as follows:
@Configuration
public static class PersistenceConfiguration {
@Bean
@Primary
public DataSource ds1() {
return new EmbeddedDatabaseBuilder().build();
}
@Bean
public DataSource ds2() {
return new EmbeddedDatabaseBuilder().build();
}
@Bean
@Primary
@Autowired
public LocalContainerEntityManagerFactoryBean emfb(@Qualifier("ds1") DataSource ds, EntityManagerFactoryBuilder emfb) {
return emfb.dataSource(ds)
.packages(DemoApplication.class)
.persistenceUnit("ds1")
.build();
}
@Bean
@Autowired
public LocalContainerEntityManagerFactoryBean emfb2(@Qualifier("ds2") DataSource ds, EntityManagerFactoryBuilder emfb) {
return emfb.dataSource(ds)
.packages(DemoApplication.class)
.persistenceUnit("ds2")
.build();
}
}
Solution 2
The error is indicating that at some point in the application, a bean is being injected by the type DataSource
and not being qualified by name at that point.
It does not matter that you have added @Qualifier
in one location. The injection is failing in some other location that has not been qualified. It's not your fault though because that location is in Spring Boot's DataSourceAutoConfiguration
which you should be able to see in your stack trace, below the piece that you have posted.
I would recommend excluding DataSourceAutoConfiguration
i.e. @SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
. Otherwise, this configuration is only being applied to the bean you have made @Primary
. Unless you know exactly what that is, it is likely to result in subtle and unexpected differences in behaviour between your DataSource
s.
Related videos on Youtube
Tuomas Toivonen
Updated on September 15, 2022Comments
-
Tuomas Toivonen about 1 year
I'm building JPA configuration with multiple persistence units using different in-memory datasources, but the configuration fails resolving the qualified datasource for entity manager factory bean with the following error:
*************************** APPLICATION FAILED TO START *************************** Description: Parameter 0 of method emfb in datasources.Application$PersistenceConfiguration required a single bean, but 2 were found: - ds1: defined by method 'ds1' in class path resource [datasources/Application$PersistenceConfiguration.class] - ds2: defined by method 'ds2' in class path resource [datasources/Application$PersistenceConfiguration.class] Action: Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed
Here is the sample application
package datasources; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import javax.persistence.PersistenceContextType; import javax.sql.DataSource; import javax.ws.rs.ApplicationPath; import javax.ws.rs.GET; import javax.ws.rs.Path; import org.apache.log4j.Logger; import org.glassfish.jersey.server.ResourceConfig; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; import org.springframework.stereotype.Component; @Configuration @EnableAutoConfiguration(exclude = { // HibernateJpaAutoConfiguration.class, // DataSourceAutoConfiguration.class JtaAutoConfiguration.class }) @ComponentScan public class Application { public static void main(String[] args) { new SpringApplicationBuilder(Application.class) .build() .run(args); } @Component @Path("/ds") public static class DsApi { private final static Logger logger = Logger.getLogger(DsApi.class); @Autowired(required = false) @Qualifier("ds1") private DataSource ds; @GET public String ds() { logger.info("ds"); return ds.toString(); } } @Component @Path("/em") public static class EmApi { private final static Logger logger = Logger.getLogger(EmApi.class); @PersistenceContext(unitName = "ds2", type = PersistenceContextType.TRANSACTION) private EntityManager em; @GET public String em() { logger.info("em"); return em.toString(); } } @Configuration @ApplicationPath("/jersey") public static class JerseyConfig extends ResourceConfig { public JerseyConfig() { register(DsApi.class); register(EmApi.class); } } @Configuration public static class PersistenceConfiguration { @Bean @Qualifier("ds1") public DataSource ds1() { return new EmbeddedDatabaseBuilder().build(); } @Bean @Qualifier("ds2") public DataSource ds2() { return new EmbeddedDatabaseBuilder().build(); } @Bean @Primary @Autowired public LocalContainerEntityManagerFactoryBean emfb(@Qualifier("ds1") DataSource ds, EntityManagerFactoryBuilder emfb) { return emfb.dataSource(ds) .packages(Application.class) .persistenceUnit("ds1") .build(); } @Bean @Autowired public LocalContainerEntityManagerFactoryBean emfb2(@Qualifier("ds2") DataSource ds, EntityManagerFactoryBuilder emfb) { return emfb.dataSource(ds) .packages(Application.class) .persistenceUnit("ds2") .build(); } } }
-
Tuomas Toivonen almost 7 yearsThis will inject ds1 to emfb2, which needs ds2
-
Arpit Aggarwal almost 7 yearsIt should not as we are specifying the
@Qualifier("ds2")
while creatingemfb2
-
Tuomas Toivonen almost 7 yearsIndeed, you are right! But why Qualifier requires Primary to work? Also, why the same wiring worked fine without Primary, if those ds methods returned some other type instead (And of course emfb methods refactored to consume that type)?
-
Arpit Aggarwal almost 7 years
-
granadaCoder about 4 yearsCould you please add the import statement(s)?