Java 使用SpringMVC和Hibernate在多租户数据库应用程序中动态添加租户

Java 使用SpringMVC和Hibernate在多租户数据库应用程序中动态添加租户,java,spring,hibernate,spring-mvc,multi-tenant,Java,Spring,Hibernate,Spring Mvc,Multi Tenant,我正在开发使用多租户数据库配置的web应用程序 我想动态添加租户 我添加了主控制器来创建主模式,该模式保存动态创建的租户记录 但问题是,当我请求创建租户时,它转到了MultitenantConnectionProvider我在那个里创建了数据库,但在数据库中,我想扫描包com.appointment.schedular.model.tenant,并在ne数据库中创建表 MasterDatabaseConfig.java TenantDatabaseConfig.java @配置 @启用事务管理

我正在开发使用多租户数据库配置的web应用程序

我想动态添加租户

我添加了主控制器来创建主模式,该模式保存动态创建的租户记录

但问题是,当我请求创建租户时,它转到了
MultitenantConnectionProvider
我在那个里创建了数据库,但在数据库中,我想扫描包
com.appointment.schedular.model.tenant
,并在ne数据库中创建表

MasterDatabaseConfig.java TenantDatabaseConfig.java
@配置
@启用事务管理
@组件扫描(“com.appointment.schedular.tenant”)
@授权代理(
entityManagerFactoryRef=“租户管理者”,
transactionManagerRef=“租户交易管理器”,
basePackages={“com.appointment.schedular.dao.tenant”})
@PropertySource(“类路径:application.properties”)
公共类租户DatabaseConfig{
@自动连线
私人环境;
@豆子
公共JpaVendorAdapter JpaVendorAdapter(){
返回新的HibernateJavaEndorapter();
}
@Bean(name=“tenantDataSource”)
公共数据源租户数据源(){
DriverManager数据源dataSource=新的DriverManager数据源();
setDriverClassName(springEnvironment.getProperty(“tenant.dataSource.classname”);
setUrl(springEnvironment.getProperty(“tenant.dataSource.url”)+“xy”+“?createDatabaseIfNotExist=true”);
setUsername(springEnvironment.getProperty(“tenant.dataSource.user”);
setPassword(springEnvironment.getProperty(“tenant.dataSource.password”);
返回数据源;
}
@Bean(name=“租户管理者”)
公共LocalContainerEntityManagerFactoryBean entityManagerFactory(
多租户连接提供程序connectionProvider,
CurrentTenantIdentifier解析程序tenantResolver){
LocalContainerEntityManagerFactoryBean emfBean=新的LocalContainerEntityManagerFactoryBean();
setDataSource(tenantDataSource());
emfBean.setPackagesToScan(“com.appointment.schedular.model.tenant”);
setJpaVendorAdapter(jpaVendorAdapter());
映射属性=新的HashMap();
properties.put(org.hibernate.cfg.Environment.MULTI_-TENANT,MULTI-tenacysttrategy.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.dial”),springEnvironment.getProperty(“hibernate.dial”
,“org.hibernate.dialogue.mysqldialogue”);
properties.put(“hibernate.show\u sql”),springEnvironment.getProperty(“hibernate.show\u sql”
,“正确”);
properties.put(“hibernate.format\u sql”),springEnvironment.getProperty(“hibernate.format\u sql”
,“正确”);
properties.put(“hibernate.hbm2ddl.auto”),springEnvironment.getProperty(“hibernate.hbm2ddl.auto”
更新);;
emfBean.setJpaPropertyMap(属性);
emfBean.setPersistenceUnitName(“主”);
返回emfBean;
}
@Bean(name=“tenantTransactionManager”)
公共JpaTransactionManager事务管理器(实体管理器工厂租户管理器){
JpaTransactionManager transactionManager=新的JpaTransactionManager();
transactionManager.SetEntityManager工厂(租户管理者);
返回事务管理器;
}
}
MultitenantConnectionProviderImpl.java
@SuppressWarnings(“串行”)
@组成部分
@PropertySource(“类路径:application.properties”)
公共类MultiTenantConnectionProviderImpl扩展了基于抽象数据源的MultiTenantConnectionProviderImpl实现了ApplicationListener{
@自动连线
私人环境;
@自动连线
私人租客道租客道;
@自动连线
@限定符(“租户数据源”)
数据源主数据源;
/*@自动连线
@限定符(“租户管理者”)
实体管理器*/
私有最终映射=新HashMap();
@凌驾
ApplicationEvent(ContextRefreshedEvent ContextRefreshedEvent)上的公共无效{
init();
}
私有void init(){
List tenants=tenantDao.findAll();
适用于(租户:租户){
DataSource genDatasource=constructDataSource(tenant.getTenantKey());
map.put(tenant.getTenantKey(),genDatasource);
/*
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean
=新的LocalContainerEntityManagerFactoryBean();
setDataSource(genDatasource);
entityManagerFactoryBean.setPersistenceProviderClass(HibernatePersistenceProvider.class);
JpaVendorAdapter=new HibernateJpaVendorAdapter();
setJpaVendorAdapter(vendorAdapter);
entityManagerFactoryBean.setPackagesToScan(新字符串[]{“com.appointment.schedular.model.tenant”});
映射属性=新的HashMap();
properties.put(“hibernate.ejb.naming_strategy”、“org.hibernate.cfg.ImprovedNamingStrategy”);
properties.put(“hibernate.dial”),springEnvironment.getProperty(“hibernate.dial”
,“org.hibernate.dialogue.mysqldialogue”);
properties.put(“hibernate.show\u sql”),springEnvironment.getProperty(“hibernate.show\u sql”
,“正确”);
properties.put(“hibernate.format\u sql”),springEnvironment.getProperty(“hibernate.format\u sql”
,“正确”);
properties.put(“hibernate.hbm2ddl.auto”),springEnvironment.getProperty(“hibernate.hbm2ddl.auto”
更新);;
entityManagerFactoryBean.setJ
@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;
   }
}
@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", 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"));
      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;
   }
}
@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", 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"));

         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));
   }
}
@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);
    }
}
@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, 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", 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"));
      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));
   }
}