[英]How to make multitenancy in Spring Boot when WebMvcAutoConfiguration$EnableWebMvcConfiguration needs only one entityManager
I'm creating application with dynamic multitenancy. 我正在创建具有动态多租户的应用程序。 Master database contains table with connections to tenants' dbs. 主数据库包含与租户数据库连接的表。
Everithing looks ok. 一切都很好。 But spring boot application fails because of: 但是由于以下原因,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
I marked one bean as primary but this didn't help: 我将一个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;
}
}
And tenants configs: 和租户配置:
@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;
}
It looks like bug in WebMvcAutoConfiguration$EnableWebMvcConfiguration when it ignores @Primary 忽略@Primary时,它看起来像WebMvcAutoConfiguration $ EnableWebMvcConfiguration中的错误
Resolution was simple. 解决方法很简单。 I marked masterTransactionManager with @Primary annotation but not masterEntityManagerFactory bean. 我用@Primary批注标记了masterTransactionManager,但没有标记masterEntityManagerFactory bean。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.