【问题标题】:How to create multiple Jdbctemplate beans, pointing to different Mysql databases, in runtime?如何在运行时创建多个 Jdbctemplate bean,指向不同的 Mysql 数据库?
【发布时间】:2020-11-04 15:30:01
【问题描述】:

我需要为我的项目创建一个基于 SAAS 的版本:当新客户注册时,将为该特定客户创建一个新数据库;在我的 Spring Boot 代码中,需要在运行时为该客户端注册一个新的 JdbcTemplate bean。

已完成数据库部分的创建,但我需要知道是否有办法在运行时创建新的自定义 Jdbctemplate bean。

我已经有了为所有查询添加数据库前缀的想法,但我想知道还有其他可行的选择。

【问题讨论】:

  • 动态 JdbcTemplate 也将需要动态 DataSource,而后者又需要动态数据库凭据。您能否确认您是否已经在运行时为每个新客户端提供了可发现的数据库凭据?我将能够相应地分享解决方案大纲。
  • 在我们的 SAAS 模型中,每当注册新客户端时,都会使用脚本创建数据库。因此,我们将有一个主表来映射客户端 ID 和数据库名称。因此,客户端 ID 将作为参数发送给所有 API,基于该客户端 ID,需要使用相应客户端 ID 的数据库动态创建 JDBCTemplate。

标签: mysql spring-boot spring-jdbc jdbctemplate


【解决方案1】:

假设您已经找到了新的数据库凭据,下面的大纲将为您提供每个客户端的 JdbcTemplate。在以下代码中,所有相关数据点均以 cmets 的形式提及。

/**
 * uses a REQUEST scope with proxyMode as TARGET_CLASS to ensure wiring in spring context works
 * as it should for each and every request while caching created templates for full reuse.
 */
@Component
@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class TemplateResolver {
    @Autowired
    private TemplateCache cache;

    /**
     * You can inject current request OR any other bean for that matter here.
     * Assuming you are using Spring authenticaiotn object to get access to
     * current clientId. Adjust accordingly for your own use case
     */
    @Autowired
    private Authentication authentication;

    public JdbcTemplate getTemplate() {
        // TODO - extract this clientId from authenticaiton or any other means as appropriate
        Strnig clientId = null /* write your logic here */;
        return cache.getTemplate(clientId);
    }

}

/**
 * Caches created JdbcTemplate beans for further loockup and reuse as needed
 */
@Component
public class TemplateCache {
    private Map<String, JdbcTemplate> templates = new HashMap<String, JdbcTemplate>();

    @Autowired
    private ApplicationContext appContext;

    public JdbcTemplate getTemplate(String clientId) {
        if (templates.contains(clientId))
            return templates.get(clientId);
        return buildTemplate(clientId);
    }

    /**
     * This is a synchronized method to ensure concurrent requests DO NOT end
     * up creating multiple JdbcTempate objects.
     */
    private JdbcTemplate buildTemplate(String id) {
        // double check to ensure it's not already created by any other
        // concurrent request which finished its execution just now
        if (templates.contains(clientId))
            return templates.get(clientId);
        templates.put(clientId, appContext.getBean(JdbcTemplate.class, id));
        return templates.get(clientId);
    }
}

/**
 * uses a REQUEST scope with proxyMode as TARGET_CLASS to ensure wiring in spring context works
 * as it should for each and every request while caching created templates for full reuse.
 */
@Component
@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class TemplateResolver {
    @Autowired
    private TemplateCache cache;

    /**
     * You can inject current request OR any other bean for that matter here.
     * Assuming you are using Spring authenticaiotn object to get access to
     * current clientId. Adjust accordingly for your own use case
     */
    @Autowired
    private Authentication authentication;

    public JdbcTemplate getTemplate() {
        // TODO - extract this clientId from authenticaiton or any other means as appropriate
        String clientId = null /* write your logic here */;
        return cache.getTemplate(clientId);
    }

}

一旦完成上述操作,您必须注入 TemplateResolver 而不是直接在现有 bean 中注入 JdbcTempalate。每次调用JdbcTemplate 的任何方法时,将其替换为resolver.getTemplate().existingTemplateCall(...)

【讨论】:

  • 在我们的 SAAS 模型中,每当注册新客户端时,都会使用脚本创建数据库。因此,我们将有一个主表来映射客户端 ID 和数据库名称。因此,客户端 ID 将作为参数发送到所有 API,基于该客户端 ID,需要使用相应客户端 ID 的数据库动态创建 JDBCTemplate。是否可以针对上述流程修改上述解决方案?
猜你喜欢
  • 1970-01-01
  • 2010-09-12
  • 1970-01-01
  • 2022-10-16
  • 2011-02-16
  • 1970-01-01
  • 2012-05-22
  • 1970-01-01
  • 2013-05-24
相关资源
最近更新 更多