当WebMvcAutoConfiguration $ EnableWebMvcConfiguration只需要一个entityManager时,如何在Spring Boot中进行多租户

时间:2016-12-07 16:37:39

标签: java spring spring-boot spring-data-jpa multi-tenant

我正在使用动态多租户创建应用程序。主数据库包含与租户连接的表格' DBS。

Everithing看起来不错。但春季启动应用程序失败的原因是:

***************************
APPLICATION FAILED TO START
***************************

Description:

Method requestMappingHandlerMapping in org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration$EnableWebMvcConfiguration required a single bean, but 2 were found:
    - masterEntityManagerFactory: defined by method 'masterEntityManagerFactory' in class path resource [com/dimanex/api/config/MasterDatabaseConfig.class]
    - tenantEntityManagerFactory: defined by method 'tenantEntityManagerFactory' in class path resource [com/dimanex/api/config/TenantsDatabaseConfig.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


Process finished with exit code 1

我将一个bean标记为主要bean,但这没有帮助:

@Configuration
@EnableConfigurationProperties(JpaProperties.class)
@EnableJpaRepositories(
    entityManagerFactoryRef = MasterDatabaseConfig.MASTER_ENTITY_MANAGER_FACTORY_NAME,
    transactionManagerRef = MasterDatabaseConfig.MASTER_TRANSACTION_MANAGER_NAME,
    basePackages = {"com.dimanex.api.repository.master"})
@EnableTransactionManagement
public class MasterDatabaseConfig {

    public static final String MASTER_ENTITY_MANAGER_FACTORY_NAME = "masterEntityManagerFactory";
    public static final String MASTER_TRANSACTION_MANAGER_NAME = "masterTransactionManager";

    @Bean(destroyMethod = "close")
    public DataSource masterDataSource(@Value("${spring.datasource.url}") String url,
                                       @Value("${spring.datasource.dataSourceClassName}") String dataSourceClassName,
                                       @Value("${spring.datasource.username}") String user,
                                       @Value("${spring.datasource.password}") String password) {

        log.debug("Configuring datasource {} {} {}", dataSourceClassName, url, user);
        HikariConfig config = new HikariConfig();
        config.setDataSourceClassName(dataSourceClassName);
        config.addDataSourceProperty("url", url);
        config.addDataSourceProperty("user", user);
        config.addDataSourceProperty("password", password);
        return new HikariDataSource(config);
    }

    @Bean(name = MASTER_ENTITY_MANAGER_FACTORY_NAME)
    public LocalContainerEntityManagerFactoryBean masterEntityManagerFactory(DataSource masterDataSource,
                                                                             JpaProperties jpaProperties) {
        JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
        LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
        em.setDataSource(masterDataSource);
        em.setPackagesToScan(new String[]{MasterBaseObject.class.getPackage().getName()});
        em.setJpaVendorAdapter(vendorAdapter);

        em.setJpaProperties(new Properties(){{
            final Properties self = this;
            jpaProperties.getHibernateProperties(masterDataSource).forEach((k, v) -> self.setProperty(k, v));
        }});

        em.setPersistenceUnitName("master");

        return em;
    }

    @Bean(name = MASTER_TRANSACTION_MANAGER_NAME)
    @Primary
    public JpaTransactionManager masterTransactionManager(@Qualifier(MASTER_ENTITY_MANAGER_FACTORY_NAME) EntityManagerFactory masterEntityManagerFactory){
        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(masterEntityManagerFactory);
        return transactionManager;
    }

}

租户配置:

@Configuration
@EnableConfigurationProperties(JpaProperties.class)
@EnableJpaRepositories(
    entityManagerFactoryRef = TenantsDatabaseConfig.TENANT_ENTITY_MANAGER_FACTORY_NAME,
    transactionManagerRef = TenantsDatabaseConfig.TENANT_TRANSACTION_MANAGER_NAME,
    basePackages = {"com.dimanex.api.repository.tenant"})
@EnableTransactionManagement
public class TenantsDatabaseConfig {

    public static final String TENANT_ENTITY_MANAGER_FACTORY_NAME = "tenantEntityManagerFactory";
    public static final String TENANT_TRANSACTION_MANAGER_NAME = "tenantsTransactionManager";

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

    @Bean
    public MultiTenantConnectionProvider multiTenantConnectionProvider(@Value("${spring.datasource.dataSourceClassName}") String dataSourceClassName) {
        return new DimanexMultiTenantConnectionProvider(dataSourceClassName);
    }

    @Bean
    public CurrentTenantIdentifierResolver currentTenantIdentifierResolver() {
        return new DimanexCurrentTenantResolver();
    }

    @Bean(name = TENANT_ENTITY_MANAGER_FACTORY_NAME)
    public LocalContainerEntityManagerFactoryBean tenantEntityManagerFactory(@Value("${spring.jpa.properties.hibernate.dialect}") String hibernateDialect,
                                                                             DataSource masterDataSource,
                                                                             MultiTenantConnectionProvider connectionProvider,
                                                                             CurrentTenantIdentifierResolver tenantResolver) {

        LocalContainerEntityManagerFactoryBean emfBean = new LocalContainerEntityManagerFactoryBean();
        emfBean.setPackagesToScan(TenantBaseObject.class.getPackage().getName());
        emfBean.setJpaVendorAdapter(jpaVendorAdapter());
        emfBean.setDataSource(masterDataSource);
        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", hibernateDialect);

        emfBean.setJpaPropertyMap(properties);

        return emfBean;
    }

    @Bean(name = TENANT_TRANSACTION_MANAGER_NAME)
    public JpaTransactionManager tenantsTransactionManager(@Qualifier(TENANT_ENTITY_MANAGER_FACTORY_NAME) EntityManagerFactory tenantEntityManager) {
        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(tenantEntityManager);
        return transactionManager;
    }

当忽略@Primary时,它看起来像WebMvcAutoConfiguration $ EnableWebMvcConfiguration中的错误

1 个答案:

答案 0 :(得分:2)

分辨率很简单。我用@Primary注释标记了masterTransactionManager,但没有标记masterEntityManagerFactory bean。