Java 当WebMvcAutoConfiguration$EnableWebMVCCConfiguration只需要一个entityManager时,如何在Spring Boot中实现多租户

Java 当WebMvcAutoConfiguration$EnableWebMVCCConfiguration只需要一个entityManager时,如何在Spring Boot中实现多租户,java,spring,spring-boot,spring-data-jpa,multi-tenant,Java,Spring,Spring Boot,Spring Data Jpa,Multi Tenant,我正在创建具有动态多租户的应用程序。主数据库包含连接到租户数据库的表 一切看起来都不错。但是spring启动应用程序失败,原因是: *************************** APPLICATION FAILED TO START *************************** Description: Method requestMappingHandlerMapping in org.springframework.boot.autoconfigure.web.Web

我正在创建具有动态多租户的应用程序。主数据库包含连接到租户数据库的表

一切看起来都不错。但是spring启动应用程序失败,原因是:

***************************
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;
    }
@配置
@EnableConfigurationProperties(jpapProperties.class)
@授权代理(
entityManagerFactoryRef=TenantsDatabaseConfig.TENANT\u ENTITY\u MANAGER\u FACTORY\u NAME,
transactionManagerRef=TenantsDatabaseConfig.TENANT\u TRANSACTION\u MANAGER\u NAME,
basePackages={“com.dimanex.api.repository.tenant”})
@启用事务管理
公共类租户数据库配置{
公共静态最终字符串TENANT\u ENTITY\u MANAGER\u FACTORY\u NAME=“tenantEntityManagerFactory”;
公共静态最终字符串TENANT\u TRANSACTION\u MANAGER\u NAME=“tenantsTransactionManager”;
@豆子
公共JpaVendorAdapter JpaVendorAdapter(){
返回新的HibernateJavaEndorapter();
}
@豆子
公共MultiTenantConnectionProvider MultiTenantConnectionProvider(@Value(${spring.datasource.dataSourceClassName}”)字符串dataSourceClassName){
返回新的DimanexMultiTenantConnectionProvider(dataSourceClassName);
}
@豆子
public CurrentTenantIdentifierResolver CurrentTenantIdentifierResolver(){
返回新的DIMANExcurrentEntertResolver();
}
@Bean(名称=租户\实体\经理\工厂\名称)
public LocalContainerEntityManagerFactoryBean租户管理Factory(@Value(${spring.jpa.properties.hibernate.dial}”)字符串hibernateDialect,
数据源主数据源,
多租户连接提供程序connectionProvider,
CurrentTenantIdentifier解析程序tenantResolver){
LocalContainerEntityManagerFactoryBean emfBean=新的LocalContainerEntityManagerFactoryBean();
emfBean.setPackagesToScan(TenantBaseObject.class.getPackage().getName());
setJpaVendorAdapter(jpaVendorAdapter());
emfBean.setDataSource(masterDataSource);
映射属性=新的HashMap();
properties.put(org.hibernate.cfg.Environment.MULTI_-TENANT,MULTI-tenacysttrategy.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.dial”,hibernateDialect);
emfBean.setJpaPropertyMap(属性);
返回emfBean;
}
@Bean(名称=租户\事务\管理者\名称)
公共JpaTransactionManager租户TransactionManager(@Qualifier(租户\实体\经理\工厂\名称)实体管理工厂租户管理者){
JpaTransactionManager transactionManager=新的JpaTransactionManager();
transactionManager.SetEntityManager工厂(租户管理者);
返回事务管理器;
}

WebMvcAutoConfiguration$EnableWebMvcConfiguration忽略@Primary时,它看起来像是一个bug。解析很简单。我用@Primary注释标记masterTransactionManager,但不是masterEntityManagerFactory bean。

解析很简单。我用@Primary注释标记masterTransactionManager,但不是masterEntityManagerFactory豆子。