簡體   English   中英

當WebMvcAutoConfiguration $ EnableWebMvcConfiguration僅需要一個entityManager時,如何在Spring Boot中進行多租戶

[英]How to make multitenancy in Spring Boot when WebMvcAutoConfiguration$EnableWebMvcConfiguration needs only one entityManager

我正在創建具有動態多租戶的應用程序。 主數據庫包含與租戶數據庫連接的表。

一切都很好。 但是由於以下原因,Spring Boot應用程序失敗:

***************************
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標記為主要,但這沒有幫助:

@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中的錯誤

解決方法很簡單。 我用@Primary批注標記了masterTransactionManager,但沒有標記masterEntityManagerFactory bean。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM