Spring boot @Qualifier does not work with data sources

I am creating a JPA configuration with multiple storage units using different data sources in memory, but the configuration cannot resolve a qualified data source for factory bean entity manager 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 an example 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(); } } } 
+5
source share
3 answers

Declare one of your DataSource as @Primary .

Also you have 2 beans of the same type - LocalContainerEntityManagerFactoryBean , declare one of them @Primary , namely:

 @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(); } } 
+3
source

The error indicates that at some point in the application a bean is entered by the DataSource type and cannot be assigned a name at this point .

It doesn’t matter that you added @Qualifier in one place. The injection does not fire in any other place that has not been qualified. This is not your mistake, because this place is in the Spring Boot DataSourceAutoConfiguration , which you can see in your stack trace, below the fragment that you placed.

I would recommend excluding DataSourceAutoConfiguration ie @SpringBootApplication(exclude = DataSourceAutoConfiguration.class) . Otherwise, this configuration applies only to the bean that you created @Primary . If you don’t know exactly what it is, it can lead to subtle and unexpected behavioral differences between your DataSource s.

+3
source

Try declaring the beans data source outside the static class. I am directly in Application.java

0
source

Source: https://habr.com/ru/post/1262144/


All Articles