Java – use spring MVC and hibernate to dynamically add tenants in multi tenant database applications
•
Java
I am developing a web application that uses a multi - tenant database configuration
I want to add tenants dynamically
I added a master controller to create a master schema that contains dynamically created tenant records
But the problem is that when I request to create a tenant, it goes to the multitenantconnectionprovider where I create the database, but in the database I want to scan the package com appointment. schedular. model. Tenant and create tables in the ne database
Source code
MasterDatabaseConfig. java
@Configuration @EnableTransactionManagement @EnableJpaRepositories( basePackages = "com.appointment.schedular.dao.master",entityManagerFactoryRef = "masterEntityManager",transactionManagerRef = "masterTransactionManager" ) @PropertySource("classpath:application.properties") public class MasterDatabaseConfig { @Autowired private Environment springEnvironment; @Bean(name="masterDataSource") public DataSource masterDataSource() { DriverManagerDataSource dataSource = new DriverManagerDataSource(); dataSource.setDriverClassName(springEnvironment.getProperty("master.datasource.classname")); dataSource.setUrl(springEnvironment.getProperty("master.datasource.url") + "?createDatabaseIfNotExist=true"); dataSource.setUsername(springEnvironment.getProperty("master.datasource.user")); dataSource.setPassword(springEnvironment.getProperty("master.datasource.password")); return dataSource; } @Bean(name = "masterEntityManager") @Primary public LocalContainerEntityManagerfactorybean masterEntityManagerFactory() { LocalContainerEntityManagerfactorybean entityManagerfactorybean = new LocalContainerEntityManagerfactorybean(); entityManagerfactorybean.setDataSource(masterDataSource()); entityManagerfactorybean.setPersistenceProviderClass(HibernatePersistenceProvider.class); JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter(); entityManagerfactorybean.setJpaVendorAdapter(vendorAdapter); entityManagerfactorybean.setPackagesToScan(new String[]{"com.appointment.schedular.model.master"}); entityManagerfactorybean.setJpaProperties(getHibernateProperties()); entityManagerfactorybean.setPersistenceUnitName("master"); return entityManagerfactorybean; } private Properties getHibernateProperties() { Properties properties = new Properties(); properties.put("hibernate.dialect",springEnvironment.getProperty("hibernate.dialect","org.hibernate.dialect.MysqLDialect")); properties.put("hibernate.show_sql",springEnvironment.getProperty("hibernate.show_sql","true")); properties.put("hibernate.format_sql",springEnvironment.getProperty("hibernate.format_sql","true")); properties.put("hibernate.hbm2ddl.auto",springEnvironment.getProperty("hibernate.hbm2ddl.auto","update")); return properties; } @Bean(name = "masterTransactionManager") public JpaTransactionManager transactionManager(EntityManagerFactory masterEntityManager) { JpaTransactionManager transactionManager = new JpaTransactionManager(); transactionManager.setEntityManagerFactory(masterEntityManager); return transactionManager; } }
TenantDatabaseConfig. java
@Configuration @EnableTransactionManagement @ComponentScan("com.appointment.schedular.tenant") @EnableJpaRepositories( entityManagerFactoryRef = "tenantEntityManager",transactionManagerRef = "tenantTransactionManager",basePackages = {"com.appointment.schedular.dao.tenant"}) @PropertySource("classpath:application.properties") public class TenantDatabaseConfig { @Autowired private Environment springEnvironment; @Bean public JpaVendorAdapter jpaVendorAdapter() { return new HibernateJpaVendorAdapter(); } @Bean(name = "tenantDataSource") public DataSource tenantDataSource() { DriverManagerDataSource dataSource = new DriverManagerDataSource(); dataSource.setDriverClassName(springEnvironment.getProperty("tenant.datasource.classname")); dataSource.setUrl(springEnvironment.getProperty("tenant.datasource.url")+"xy" + "?createDatabaseIfNotExist=true"); dataSource.setUsername(springEnvironment.getProperty("tenant.datasource.user")); dataSource.setPassword(springEnvironment.getProperty("tenant.datasource.password")); return dataSource; } @Bean(name = "tenantEntityManager") public LocalContainerEntityManagerfactorybean entityManagerFactory( MultiTenantConnectionProvider connectionProvider,CurrentTenantIdentifierResolver tenantResolver) { LocalContainerEntityManagerfactorybean emfBean = new LocalContainerEntityManagerfactorybean(); emfBean.setDataSource(tenantDataSource()); emfBean.setPackagesToScan("com.appointment.schedular.model.tenant"); emfBean.setJpaVendorAdapter(jpaVendorAdapter()); Map<String,Object> properties = new HashMap<>(); properties.put(org.hibernate.cfg.Environment.MULTI_TENANT,MultiTenancyStrategy.SCHEMA); 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","update")); emfBean.setJpaPropertyMap(properties); emfBean.setPersistenceUnitName("master"); return emfBean; } @Bean(name = "tenantTransactionManager") public JpaTransactionManager transactionManager(EntityManagerFactory tenantEntityManager) { JpaTransactionManager transactionManager = new JpaTransactionManager(); transactionManager.setEntityManagerFactory(tenantEntityManager); return transactionManager; } }
MultitenantConnectionProviderImpl. java
@SuppressWarnings("serial") @Component @PropertySource("classpath:application.properties") public class MultiTenantConnectionProviderImpl extends AbstractDataSourceBasedMultiTenantConnectionProviderImpl implements ApplicationListener<ContextRefreshedEvent> { @Autowired private Environment springEnvironment; @Autowired private TenantDao tenantDao; @Autowired @Qualifier("tenantDataSource") DataSource masterDataSource; /*@Autowired @Qualifier("tenantEntityManager") EntityManager*/ private final Map<String,DataSource> map = new HashMap<>(); @Override public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) { init(); } private void init() { List<Tenant> tenants = tenantDao.findAll(); for (Tenant tenant : tenants) { DataSource genDatasource = constructDataSource(tenant.getTenantKey()); map.put(tenant.getTenantKey(),genDatasource); /* LocalContainerEntityManagerfactorybean entityManagerfactorybean = new LocalContainerEntityManagerfactorybean(); entityManagerfactorybean.setDataSource(genDatasource); entityManagerfactorybean.setPersistenceProviderClass(HibernatePersistenceProvider.class); JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter(); entityManagerfactorybean.setJpaVendorAdapter(vendorAdapter); entityManagerfactorybean.setPackagesToScan(new String[]{"com.appointment.schedular.model.tenant"}); Map<String,Object> properties = new HashMap<>(); properties.put("hibernate.ejb.naming_strategy","org.hibernate.cfg.ImprovedNamingStrategy"); properties.put("hibernate.dialect","org.hibernate.dialect.MysqLDialect")); properties.put("hibernate.show_sql","true")); properties.put("hibernate.format_sql","true")); properties.put("hibernate.hbm2ddl.auto","update")); entityManagerfactorybean.setJpaPropertyMap(properties); */ } } private DataSource constructDataSource(String dbName) { DriverManagerDataSource dataSource = new DriverManagerDataSource(); dataSource.setDriverClassName(springEnvironment.getProperty("tenant.datasource.classname")); dataSource.setUrl(springEnvironment.getProperty("tenant.datasource.url") + dbName+ "?createDatabaseIfNotExist=true"); dataSource.setUsername(springEnvironment.getProperty("tenant.datasource.user")); dataSource.setPassword(springEnvironment.getProperty("tenant.datasource.password")); try { dataSource.getConnection().createStatement().execute("CREATE DATABASE IF NOT EXISTS " + dbName); } catch (Exception ex) { System.out.println(ex); } return dataSource; } @Override protected DataSource selectAnyDataSource() { return masterDataSource; } @Override protected DataSource selectDataSource(String key) { return map.get(key); } public void addTenant(String tenantKey) { map.put(tenantKey,constructDataSource(tenantKey)); } }
TenantController. java
@Controller @RequestMapping("/tenant") public class TenantController { @Autowired TenantDao tenantRepo; @Autowired MultiTenantConnectionProviderImpl multiTenantConnectionProviderImpl; @SuppressWarnings("rawtypes") @CrossOrigin @RequestMapping(value = "/",method = RequestMethod.POST,consumes = MediaType.APPLICATION_JSON_VALUE) public @ResponseBody String registerTenant(@RequestBody Map map) throws JsonProcessingException { ObjectMapper mapper = new ObjectMapper(); Tenant tenant = mapper.convertValue(map,Tenant.class); String tenantKey = tenant.getName().replaceAll("[^a-zA-Z]+","").toLowerCase().trim(); Optional<Tenant> prevIoUslyStored = tenantRepo.findByTenantKey(tenantKey); String response="Sorry your company name ("+tenant.getName()+")"+" is already taken"; if (!prevIoUslyStored.isPresent()) { tenant.setTenantKey(tenantKey); tenantRepo.save(tenant); multiTenantConnectionProviderImpl.addTenant(tenantKey); response = "Successfully registered,your key is " + tenantKey; return response; } return new ObjectMapper().writeValueAsString(response); } }
Solution
Replace your multitenantconnectionproviderimpl. With this code java. Create tables using injection services and configuration classes
@SuppressWarnings("serial") @Component @PropertySource("classpath:application.properties") public class MultiTenantConnectionProviderImpl extends AbstractDataSourceBasedMultiTenantConnectionProviderImpl implements ApplicationListener<ContextRefreshedEvent>,ServiceRegistryAwareService { @Autowired private Environment springEnvironment; @Autowired private TenantDao tenantDao; @Autowired @Qualifier("dataSource1") DataSource masterDataSource; @Autowired MultiTenantConnectionProvider connectionProvider; @Autowired CurrentTenantIdentifierResolver tenantResolver; @Autowired TenantDatabaseConfig tenantDatabaseConfig; private final Map<String,DataSource> map = new HashMap<>(); @Override public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) { init(); } private void init() { List<Tenant> tenants = tenantDao.findAll(); for (Tenant tenant : tenants) { map.put(tenant.getTenantKey(),constructDataSource(tenant.getTenantKey())); } } private DataSource constructDataSource(String dbName) { DriverManagerDataSource dataSource = new DriverManagerDataSource(); dataSource.setDriverClassName(springEnvironment.getProperty("tenant.datasource.classname")); dataSource.setUrl(springEnvironment.getProperty("tenant.datasource.url") + dbName+"?createDatabaseIfNotExist=true"); dataSource.setUsername(springEnvironment.getProperty("tenant.datasource.user")); dataSource.setPassword(springEnvironment.getProperty("tenant.datasource.password")); entityManagerFactory(dataSource,connectionProvider,tenantResolver); return dataSource; } public LocalContainerEntityManagerfactorybean entityManagerFactory(DataSource dataSource,MultiTenantConnectionProvider connectionProvider,CurrentTenantIdentifierResolver tenantResolver) { LocalContainerEntityManagerfactorybean emfBean = new LocalContainerEntityManagerfactorybean(); emfBean.setDataSource(dataSource); emfBean.setPackagesToScan("com.appointment.schedular.model.tenant"); emfBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter()); emfBean.setPersistenceProviderClass(HibernatePersistenceProvider.class); Map<String,MultiTenancyStrategy.DATABASE); properties.put(org.hibernate.cfg.Environment.MULTI_TENANT_CONNECTION_PROVIDER,"update")); emfBean.setJpaPropertyMap(properties); emfBean.setPersistenceUnitName(dataSource.toString()); emfBean.afterPropertiesSet(); //emfBean.setEntityManagerFactoryInterface((EntityMana)emfBean); //emfBean.setBeanName("srgsrohtak"); return emfBean; } public JpaTransactionManager transactionManager(EntityManagerFactory tenantEntityManager) { JpaTransactionManager transactionManager = new JpaTransactionManager(); transactionManager.setEntityManagerFactory(tenantEntityManager); transactionManager.afterPropertiesSet(); return transactionManager; } @Override public void injectServices(ServiceRegistryImplementor serviceRegistry) { Map lSettings = serviceRegistry.getService(ConfigurationService.class).getSettings(); DataSource localDs = (DataSource) lSettings.get("hibernate.connection.datasource"); masterDataSource = localDs; } @Override protected DataSource selectAnyDataSource() { return masterDataSource; } @Override protected DataSource selectDataSource(String key) { return map.get(key); } public void addTenant(String tenantKey) { map.put(tenantKey,constructDataSource(tenantKey)); } }
The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
二维码