【问题标题】:multi-tenant application in spring - connecting to DBspring 中的多租户应用程序 - 连接到 DB
【发布时间】:2017-12-05 20:48:05
【问题描述】:

各位专家,

我正在处理一个多租户项目。这是每个租户架构的一个表。
为此,我们正在使用 spring 和 JPA (eclipse-link)。 在这里,我们的用例是当新客户订阅我们的应用程序时,将为该客户创建一个新的数据库。

由于 spring 配置只会在启动时加载,如何在运行时加载这个新的 db 配置?

有人可以指点一下吗? 提前致谢。

BR, 小猫

【问题讨论】:

    标签: spring-data-jpa multi-tenant


    【解决方案1】:

    对于多租户,首先需要创建 MultitenantConfig.java 像下面的文件。 这里tenants.get("Musa")是我的租户名,来自application.properties文件

     @Configuration
        @EnableConfigurationProperties(MultitenantProperties.class)
        public class MultiTenantConfig extends WebMvcConfigurerAdapter {
    
            /** The Constant log. */
            private static final Logger log = LoggerFactory.getLogger(MultiTenantConfig.class);
    
            /** The multitenant config. */
            @Autowired
            private MultitenantProperties multitenantConfig;
    
            @Override
            public void addInterceptors(InterceptorRegistry registry) {
                registry.addInterceptor(new MultiTenancyInterceptor());
            }
    
            /**
             * Data source.
             *
             * @return the data source
             */
            @Bean
            public DataSource dataSource() {
    
                Map<Object, Object> tenants = getTenants();
    
                MultitenantDataSource multitenantDataSource = new MultitenantDataSource();
                multitenantDataSource.setDefaultTargetDataSource(tenants.get("Musa"));
                multitenantDataSource.setTargetDataSources(tenants);
    
                // Call this to finalize the initialization of the data source.
                multitenantDataSource.afterPropertiesSet();
    
                return multitenantDataSource;
            }
    
            /**
             * Gets the tenants.
             *
             * @return the tenants
             */
            private Map<Object, Object> getTenants() {
                Map<Object, Object> resolvedDataSources = new HashMap<>();
    
                for (Tenant tenant : multitenantConfig.getTenants()) {
                    DataSourceBuilder dataSourceBuilder = new DataSourceBuilder(this.getClass().getClassLoader());
                    dataSourceBuilder.driverClassName(tenant.getDriverClassName()).url(tenant.getUrl())
                            .username(tenant.getUsername()).password(tenant.getPassword());
                    DataSource datasource = dataSourceBuilder.build();
    
                    for (String prop : tenant.getTomcat().keySet()) {
                        try {
                            BeanUtils.setProperty(datasource, prop, tenant.getTomcat().get(prop));
                        } catch (IllegalAccessException | InvocationTargetException e) {
                            log.error("Could not set property " + prop + " on datasource " + datasource);
                        }
                    }
    
                    log.info(datasource.toString());
                    resolvedDataSources.put(tenant.getName(), datasource);
                }
    
                return resolvedDataSources;
            }
    
        }
    
    
    public class MultitenantDataSource extends AbstractRoutingDataSource {
        @Override
        protected Object determineCurrentLookupKey() {
            return TenantContext.getCurrentTenant();
        }
    }
    
    
    
    public class MultiTenancyInterceptor extends HandlerInterceptorAdapter {
    
        @Override
        public boolean preHandle(HttpServletRequest req, HttpServletResponse res, Object handler) {
            TenantContext.setCurrentTenant("Musa");
            return true;
        }
    }
    
    
    
     @ConfigurationProperties(prefix = "multitenancy")
    public class MultitenantProperties {
    
        public static final String CURRENT_TENANT_IDENTIFIER = "tenantId";
        public static final int CURRENT_TENANT_SCOPE = 0;
    
        private List<Tenant> tenants;
    
        public List<Tenant> getTenants() {
            return tenants;
        }
    
        public void setTenants(List<Tenant> tenants) {
            this.tenants = tenants;
        }    
    }
    
    
    
      public class Tenant {
    
        private String name;
        private String url;
        private String driverClassName;
        private String username;
        private String password;
        private Map<String,String> tomcat;
    
    //setter gettter
    

    public class TenantContext {
        private static ThreadLocal<Object> currentTenant = new ThreadLocal<>();
        public static void setCurrentTenant(Object tenant) {
            currentTenant.set(tenant);
        }
        public static Object getCurrentTenant() {
            return currentTenant.get();
        }
    }
    

    在 application.properties 中添加以下属性

    multitenancy.tenants[0].name=Musa
    multitenancy.tenants[0].url<url>
    multitenancy.tenants[0].username=<username>
    multitenancy.tenants[0].password=<password>
    multitenancy.tenants[0].driver-class-name=<driverclass>
    

    【讨论】:

    • 每当有新客户注册时都会创建新数据库。
    • 请忽略我之前的评论。每当有新客户注册到我们的应用程序时,都会创建一个新数据库。所以我们不会在编译期间知道在 application.properties 中配置了多少个 DB。我在想的是创建主目录(表),其中包含租户 ID 和对应的数据库详细信息,如 url 和名称....每当客户注册时,都会在此处创建条目。但问题是如何为其创建数据源。您能否让我知道如何实现这一目标。如果您认为是否有其他更好的处理方式,请告知。
    • MultitenantProperties 你可以在这个对象中配置运行时属性
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-09
    • 1970-01-01
    • 2018-06-03
    • 2021-04-24
    • 2013-12-23
    相关资源
    最近更新 更多