Java 当WebMvcAutoConfiguration$EnableWebMVCCConfiguration只需要一个entityManager时,如何在Spring Boot中实现多租户
我正在创建具有动态多租户的应用程序。主数据库包含连接到租户数据库的表 一切看起来都不错。但是spring启动应用程序失败,原因是: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
***************************
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豆子。