简体   繁体   中英

MultitenantConfiguration: No qualifying bean of type [javax.persistence.EntityManagerFactory] is defined: expected single matching bean but found 2

I'm trying to implement multitenant application. so i created two entityManager one for master and other for tenant. but i'm getting error

SEVERE: Exception sending context initialized event to listener instance of class org.springframework.web.context.ContextLoaderListener
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'customerController': Unsatisfied dependency expressed through field 'customerService': Error creating bean with name 'customerServiceImpl': Unsatisfied dependency expressed through field 'customerDao': Error creating bean with name 'customerDao': Injection of persistence dependencies failed; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [javax.persistence.EntityManagerFactory] is defined: expected single matching bean but found 2: masterEntityManager,tenantEntityManager; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'customerDao': Injection of persistence dependencies failed; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [javax.persistence.EntityManagerFactory] is defined: expected single matching bean but found 2: masterEntityManager,tenantEntityManager; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'customerServiceImpl': Unsatisfied dependency expressed through field 'customerDao': Error creating bean with name 'customerDao': Injection of persistence dependencies failed; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [javax.persistence.EntityManagerFactory] is defined: expected single matching bean but found 2: masterEntityManager,tenantEntityManager; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'customerDao': Injection of persistence dependencies failed; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [javax.persistence.EntityManagerFactory] is defined: expected single matching bean but found 2: masterEntityManager,tenantEntityManager
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:569)
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:349)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1214)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:543)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:775)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:861)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:541)
    at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:444)
    at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:326)
    at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:107)
    at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4811)
    at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5272)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:147)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1407)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1397)
    at java.util.concurrent.FutureTask.run(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at java.lang.Thread.run(Unknown Source)
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'customerServiceImpl': Unsatisfied dependency expressed through field 'customerDao': Error creating bean with name 'customerDao': Injection of persistence dependencies failed; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [javax.persistence.EntityManagerFactory] is defined: expected single matching bean but found 2: masterEntityManager,tenantEntityManager; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'customerDao': Injection of persistence dependencies failed; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [javax.persistence.EntityManagerFactory] is defined: expected single matching bean but found 2: masterEntityManager,tenantEntityManager
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:569)
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:349)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1214)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:543)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
    at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:187)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1213)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1053)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1018)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:566)
    ... 24 more
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'customerDao': Injection of persistence dependencies failed; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [javax.persistence.EntityManagerFactory] is defined: expected single matching bean but found 2: masterEntityManager,tenantEntityManager
    at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor.postProcessPropertyValues(PersistenceAnnotationBeanPostProcessor.java:357)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1214)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:543)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
    at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:187)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1213)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1053)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1018)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:566)
    ... 38 more
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [javax.persistence.EntityManagerFactory] is defined: expected single matching bean but found 2: masterEntityManager,tenantEntityManager
    at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor.findDefaultEntityManagerFactory(PersistenceAnnotationBeanPostProcessor.java:587)
    at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor.findEntityManagerFactory(PersistenceAnnotationBeanPostProcessor.java:546)
    at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor$PersistenceElement.resolveEntityManager(PersistenceAnnotationBeanPostProcessor.java:712)
    at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor$PersistenceElement.getResourceToInject(PersistenceAnnotationBeanPostProcessor.java:685)
    at org.springframework.beans.factory.annotation.InjectionMetadata$InjectedElement.inject(InjectionMetadata.java:169)
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)
    at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor.postProcessPropertyValues(PersistenceAnnotationBeanPostProcessor.java:354)
    ... 50 more

Existing Solutions

solution 1 I also tried to add annotation @Primary in MasterDatabaseConfig.java but it didn't worked.

solution2 I added @PersistenceContext(unitName="tenantEntityManager") in AbstractDao.java but it didn't worked.

@PersistenceContext(unitName="tenantEntityManager")
    private EntityManager entityManager;

Project Structure

项目结构

AppConfig.java

@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "com.appointment.schedular")
public class AppConfig extends WebMvcConfigurerAdapter {

    private TenantIdentifierInterceptorAdapter multiTenancyInterceptor;

    @Bean
    public ViewResolver viewResolver() {
        InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
        viewResolver.setViewClass(JstlView.class);
        viewResolver.setPrefix("/WEB-INF/views/");
        viewResolver.setSuffix(".jsp");
        return viewResolver;
    }

    @Override
    public void addResourceHandlers(final ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/resources/**")
        .addResourceLocations("/resources/theme1/");
    }

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**");
    }

    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
          registry.addInterceptor(multiTenancyInterceptor);
    }
}

AppInitializer.java

public class AppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class[] { AppConfig.class };
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return null;
    }

    @Override
    protected String[] getServletMappings() {
        return new String[] { "/" };
    }
}

MasterDatabaseConfig.java

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(basePackages = "com.appointment.schedular.dao.master", 
                       entityManagerFactoryRef = "masterEntityManager", 
                       transactionManagerRef = "masterTransactionManager")
@PropertySource("classpath:application2.properties")
public class MasterDatabaseConfig {

    @Autowired
    private Environment springEnvironment;

    @Bean
    public DataSource dataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName(
                springEnvironment.getProperty("master.datasource.classname", "com.mysql.jdbc.Driver"));
        dataSource.setUrl(springEnvironment.getProperty("master.datasource.url", "jdbc:mysql://localhost:3307/master")
                + "?createDatabaseIfNotExist=true");
        dataSource.setUsername(springEnvironment.getProperty("master.datasource.user", "root"));
        dataSource.setPassword(springEnvironment.getProperty("master.datasource.password", "root"));
        return dataSource;
    }

    @Bean(name = "masterEntityManager")
    //@PersistenceContext(unitName="master")
    //@Primary
    public LocalContainerEntityManagerFactoryBean masterEntityManagerFactory() {
        LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
        entityManagerFactoryBean.setDataSource(dataSource());
        entityManagerFactoryBean.setPersistenceProviderClass(HibernatePersistenceProvider.class);
        JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
        entityManagerFactoryBean.setJpaVendorAdapter(vendorAdapter);
        entityManagerFactoryBean.setPackagesToScan(new String[] { "com.appointment.schedular.model.master" });
        entityManagerFactoryBean.setJpaProperties(getHibernateProperties());
        entityManagerFactoryBean.setPersistenceUnitName("master");
        return entityManagerFactoryBean;
    }

    private Properties getHibernateProperties() {
        Properties properties = new Properties();
        properties.put("hibernate.dialect",  springEnvironment.getProperty("hibernate.dialect", "org.hibernate.dialect.MySQLDialect"));
        properties.put("hibernate.show_sql", springEnvironment.getProperty("hibernate.show_sql", "true"));
        properties.put("hibernate.format_sql", springEnvironment.getProperty("hibernate.format_sql", "true"));
        properties.put("hibernate.hbm2ddl.auto", springEnvironment.getProperty("hibernate.hbm2ddl.auto", "update"));
        return properties;
    }

    @Bean(name = "masterTransactionManager")
    public JpaTransactionManager transactionManager(EntityManagerFactory masterEntityManager) {
        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(masterEntityManager);
        return transactionManager;
    }
}

TenantDatabaseConfig.java

@Configuration
@EnableTransactionManagement
@ComponentScan("com.appointment.schedular.tenant")
@EnableJpaRepositories(
        entityManagerFactoryRef = "tenantEntityManager",
        transactionManagerRef = "tenantTransactionManager",
        basePackages = {"com.appointment.schedular.dao.tenant"})
@PropertySource("classpath:application2.properties")
public class TenantDatabaseConfig {

   @Autowired
   private Environment springEnvironment;

   @Bean
   public JpaVendorAdapter jpaVendorAdapter() {
      return new HibernateJpaVendorAdapter();
   }

   @Bean(name = "tenantEntityManager")
   //@PersistenceContext(unitName="tenant")
   public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource,
           MultiTenantConnectionProvider connectionProvider,
           CurrentTenantIdentifierResolver tenantResolver) {
      LocalContainerEntityManagerFactoryBean emfBean = new LocalContainerEntityManagerFactoryBean();
      emfBean.setDataSource(dataSource);
      emfBean.setPackagesToScan("com.appointment.schedular.model.tenant");
      emfBean.setJpaVendorAdapter(jpaVendorAdapter());
      Map<String, Object> properties = new HashMap<>();
      properties.put(org.hibernate.cfg.Environment.MULTI_TENANT, MultiTenancyStrategy.DATABASE);
      properties.put(org.hibernate.cfg.Environment.MULTI_TENANT_CONNECTION_PROVIDER, connectionProvider);
      properties.put(org.hibernate.cfg.Environment.MULTI_TENANT_IDENTIFIER_RESOLVER, tenantResolver);
      properties.put("hibernate.ejb.naming_strategy", "org.hibernate.cfg.ImprovedNamingStrategy");
        properties.put("hibernate.dialect", springEnvironment.getProperty("hibernate.dialect"
              , "org.hibernate.dialect.MySQLDialect"));
      properties.put("hibernate.show_sql", springEnvironment.getProperty("hibernate.show_sql"
              , "true"));
      properties.put("hibernate.format_sql", springEnvironment.getProperty("hibernate.format_sql"
              , "true"));
      properties.put("hibernate.hbm2ddl.auto", springEnvironment.getProperty("hibernate.hbm2ddl.auto"
              , "update"));
      emfBean.setJpaPropertyMap(properties);
      return emfBean;
   }

   @Bean(name = "tenantTransactionManager")
   public JpaTransactionManager transactionManager(EntityManagerFactory tenantEntityManager) {
      JpaTransactionManager transactionManager = new JpaTransactionManager();
      transactionManager.setEntityManagerFactory(tenantEntityManager);
      return transactionManager;
   }
}

CurrentTenantResolver.java

@Component
public class CurrentTenantResolverImpl implements CurrentTenantIdentifierResolver {

    @Override
    public String resolveCurrentTenantIdentifier() {
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        if (requestAttributes != null) {
            String identifier = (String) requestAttributes.getAttribute("Current_Tenant",
                    RequestAttributes.SCOPE_REQUEST);
            if (identifier != null) {
                return identifier;
            }
        }
        return "";
    }

    @Override
    public boolean validateExistingCurrentSessions() {
        return true;
    }

}

AbstractDao.java

public abstract class AbstractDao<PK extends Serializable, T> {

    private final Class<T> persistentClass;

    @SuppressWarnings("unchecked")
    public AbstractDao() {
        this.persistentClass = (Class<T>) ((ParameterizedType) this.getClass().getGenericSuperclass())
                .getActualTypeArguments()[1];
    }

    /*
     * @Autowired 
     * private SessionFactory sessionFactory;
     */

    @PersistenceContext/*(unitName="tenantEntityManager")*/
    private EntityManager entityManager;


    /*
     protected Session getSession() {
        return sessionFactory.getCurrentSession();
     }
    */

    protected EntityManager getSession() {
        return this.entityManager;
     }

    public T getByKey(PK key) {
        //return (T) getSession().get(persistentClass, key);
        return (T) getSession().find(persistentClass, key);
    }

    public void persist(Object entity) {
        getSession().persist(entity);
    }

    public T save(T entity) {
        //getSession().save(entity);
        getSession().persist(entity);
        return entity;
    }

    public void delete(T entity) {
        //getSession().delete(entity);
        getSession().remove(entity);
    }

    public void update(T entity){
        getSession().merge(entity);
    }

    protected Criteria createEntityCriteria() {
        //return getSession().createCriteria(persistentClass);
        //SessionFactory sessionFactory = entityManagerFactory.unwrap(SessionFactory.class);
        Session session = entityManager.unwrap(Session.class);
        //SessionFactory sessionFactory = session.getSessionFactory();
        return session.createCriteria(persistentClass);
    }

}

MultiTenantConectionProvider.java

@Component
@PropertySource("classpath:application2.properties")
public class MultiTenantConnectionProviderImpl extends AbstractDataSourceBasedMultiTenantConnectionProviderImpl
        implements ApplicationListener<ContextRefreshedEvent> {


    private static final long serialVersionUID = -4294730719204740428L;

    @Autowired
    private Environment springEnvironment;

    @Autowired
    private TenantDao tenantRepo;

    @Autowired
    DataSource masterDataSource;

    private final Map<String, DataSource> map = new HashMap<>();

    @Override
    public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
        init();
    }

    private void init() {
        List<Tenant> tenants = tenantRepo.findAll();
        for (Tenant tenant : tenants) {
            map.put(tenant.getTenantKey(), constructDataSource(tenant.getTenantKey()));
        }
    }

    private DataSource constructDataSource(String dbName) {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName(
                springEnvironment.getProperty("tenant.datasource.classname", "com.mysql.jdbc.Driver"));
        dataSource.setUrl(springEnvironment.getProperty("tenant.datasource.url", "jdbc:mysql://localhost:3307/")
                + dbName + "?createDatabaseIfNotExist=true");
        dataSource.setUsername(springEnvironment.getProperty("tenant.datasource.user", "root"));
        dataSource.setPassword(springEnvironment.getProperty("tenant.datasource.password", "root"));

        try {
            dataSource.getConnection().createStatement().execute("CREATE DATABASE IF NOT EXISTS " + dbName);

        } catch (Exception ex) {
            System.out.println(ex);
        }
        return dataSource;
    }

    @Override
    protected DataSource selectAnyDataSource() {
        return masterDataSource;
    }

    @Override
    protected DataSource selectDataSource(String key) {
        return map.get(key);
    }

    public void addTenant(String tenantKey) {
        map.put(tenantKey, constructDataSource(tenantKey));
    }
}

TenantIdentifierInterceptorAdaptor.java

@Component
public class TenantIdentifierInterceptorAdapter extends HandlerInterceptorAdapter  {

    @Autowired
       private TenantDao tenantRepo;


    @SuppressWarnings("unchecked")
       @Override
       public boolean preHandle(HttpServletRequest req, HttpServletResponse res, Object handler) throws Exception {

          Map<String, Object> pathVars
                  = (Map<String, Object>) req.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);

          if (pathVars.containsKey("tenantId")) {
             String tenantId = pathVars.get("tenantId").toString();

             Optional<Tenant> thisTenant = tenantRepo.findByTenantKey(tenantId);
             if (thisTenant.isPresent()) {
                req.setAttribute("Current_Tenant", thisTenant.get().getTenantKey());
                return true;
             } else {
                return false;
             }
          } else {
             return true;
          }

       }

}

You are implementing two entitymanger and in your abstract dao when you implementing entitymanager instance it will confuse which one to use.

so use

@PersistenceContext(unitName="tenantEntityManager")
 private EntityManager entityManager;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM