简体   繁体   English

在基于 Spring Boot 的应用程序中使用 REST API 输入在运行时设置模式名称

[英]Set schema name at run time using REST API input in spring boot based application

In a postgres database, the schema name is set as the buyer id (so, if buyer id is buyer_2213 then schema name will be buyer_2213 ).postgres数据库中,模式名称被设置为买家 ID (因此,如果买家 ID 是buyer_2213,那么模式名称将是buyer_2213 )。 These schemas have multiple tables and these tables have common structure for all schemas.这些模式有多个表,并且这些表具有所有模式的通用结构。

Now, I am writing REST API using spring boot to get buyer data from these schema, but since schema name is buyer id dependent, I have to write manual queries and could not use JPA features for that.现在,我正在使用 Spring Boot 编写 REST API 以从这些架构中获取买家数据,但由于架构名称依赖于买家 ID ,因此我必须编写手动查询并且无法为此使用 JPA 功能。

Is there any way to set schema name to entity using REST API request parameters.有什么方法可以使用 REST API 请求参数将架构名称设置为实体。 So, in below entity, can we set schema using buyerId passed at API call defined in BuyerController:因此,在下面的实体中,我们可以使用在buyerController 中定义的API 调用中传递的buyerId来设置模式

  @Entity
  @Table(name="buyer_table", schema="set_it_using_API_input")
  public class BuyerTable{
    ...
  }


  @RestController
  @RequestMapping("/buyer")
  public class BuyerController{
    
    @GetMapping("/{buyerId}")
    public void getBuyerData(@PathVariable(required = true) String buyerId){
      ...
    }
  }

Also, the buyer Id is not same as logged in user(consider this situation as admin user who is trying to get buyer's details) and will be provided as API request param only(or any other way as API input).此外,买家 ID 与登录用户不同(将此情况视为尝试获取买家详细信息的管理员用户),并且将仅作为 API 请求参数提供(或作为 API 输入的任何其他方式)。 Hence I could not find relevance to this因此我找不到与相关的内容

I've finally found a working solution for this.我终于找到了一个可行的解决方案。 This solution uses configurations mostly from here but is more specific to the my question requirements.此解决方案主要使用此处的配置,但更具体地针对我的问题要求。

The idea is obviously to use AbstractDataSource and data source configurations are pretty much similar to what shown in here with just the schema name will be set using a setter which can be called from inside API logic.这个想法显然是使用AbstractDataSource并且数据源配置与此处显示的非常相似,只是模式名称将使用可以从 API 逻辑内部调用的setter 设置
First, we need to write an implementation of AbstractDataSource which will pretty much look like this:首先,我们需要编写一个AbstractDataSource的实现,它看起来像这样:

public class BuyerSchemaDataSource extends AbstractDataSource {

    private LoadingCache<String, DataSource> dataSources = createCache();

    public void setSchemaName(String schemaName){
        this.schemaName = schemaName;
    }

    @Override public Connection getConnection() throws SQLException {
        try {
            return determineTargetDataSource().getConnection();
        } catch (ExecutionException e) {
            //print exception
            return null;
        }
    }

    @Override public Connection getConnection(String username, String password)
        throws SQLException {
        try {
            return determineTargetDataSource().getConnection(username,password);
        } catch (ExecutionException e) {
            //print exception
            return null;
        }
    }

    private DataSource determineTargetDataSource() throws ExecutionException {
        if(!utils.isNullOrEmpty(schemaName)){
            return dataSources.get(schemaName);
        }
        return buildDataSourceFromSchema(null);
    }

    private LoadingCache<String, DataSource> createCache(){
        return CacheBuilder.newBuilder()
            .maximumSize(100)
            .expireAfterWrite(10, TimeUnit.MINUTES)
            .build(new CacheLoader<String, DataSource>() {
                @Override public DataSource load(String key) throws Exception {
                    return buildDataSourceFromSchema(key);
                }
            });
    }

    private DataSource buildDataSourceForSchema(String schema) {
        // e.g. of property: "jdbc:postgresql://localhost:5432/mydatabase?currentSchema="
        String url = env.getRequiredProperty("spring.datasource.url") + schema;
        return DataSourceBuilder.create()
            .driverClassName(env.getRequiredProperty("spring.datasource.driverClassName"))
            [...]
            .url(url)
            .build();
    }
}


Now, just like any other data source this can be used in a spring configuration file like this: 现在,就像任何其他数据源一样,它可以在 spring 配置文件中使用,如下所示:
 @Configuration @EnableTransactionManagement @EnableJpaRepositories(entityManagerFactoryRef = "schemaSpecificEntityManagerFactory", transactionManagerRef = "schemaSpecificTransactionManager") public class SchemaSpecificConfig { @Bean(name = "schemaSpecificDataSource") public DataSource schemaSpecificDataSource(){ return new BuyerSchemaDataSource(); } @Bean(name = "schemaSpecificEntityManagerFactory") public LocalContainerEntityManagerFactoryBean schemaSpecificEntityManagerFactory( EntityManagerFactoryBuilder builder, @Qualifier("schemaSpecificDataSource") DataSource dataSource) { HashMap<String, Object> properties = new HashMap<>(); properties.put("hibernate.hbm2ddl.auto", "update"); properties.put("hibernate.dialect", "org.hibernate.dialect.PostgreSQLDialect"); return builder.dataSource(dataSource).properties(properties) .persistenceUnit("SchemaSpecific").build(); } @Bean(name = "schemaSpecificTransactionManager") public PlatformTransactionManager schemaSpecificTransactionManager( @Qualifier("schemaSpecificEntityManagerFactory") EntityManagerFactory schemaSpecificEntityManagerFactory) { return new JpaTransactionManager(schemaSpecificEntityManagerFactory); } }

Now, the setSchema() method defined in BuyerSchemaDataSource can be called from inside API logic in controller. 现在,可以从控制器中的 API 逻辑内部调用在 BuyerSchemaDataSource 中定义的 setSchema() 方法。

This looks like a bad way to solve it but I have not find anything better than this and all suggestions/edits are appreciated. 这看起来是一个糟糕的解决方法,但我没有找到比这更好的方法,并且感谢所有建议/编辑。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM