【问题标题】:How to change GraphQL Schema at runtime in Spring Boot?如何在 Spring Boot 运行时更改 GraphQL Schema?
【发布时间】:2023-01-24 03:21:41
【问题描述】:

有已经实现了 GraphQL 的 Spring Boot 后端,我有一个动态数据库,用户可以使用它来创建新的表和列。根据这些信息,我需要实现一个可以在运行时更改到我的后端的动态模式。我发现 GraphQL Schema 一旦构建就不可更改,因此我无法执行任何更改。但是在 documentation 中它说我们可以使用模式转换器来实现这一点。所以,我尝试了文档方法并使用了架构转换器GraphQLTypeVisitorStub.

@PostConstruct
public void init() throws SQLException, IOException, URISyntaxException {
    Path graphQLSchemaFilePath = Paths.get(
            Objects.requireNonNull(this.getClass().getResource(GRAPHQL_SCHEMA_FILE)).toURI());
    GraphQLSchema localSchema = buildSchema(graphQLSchemaFilePath);
    graphQLSchema = localSchema;
    this.graphQL = GraphQL.newGraphQL(graphQLSchema).build();

    // below code is to test if in runtime does it add the new types
    // normally these methods are not going to be used 
    // these methods will get triggered from other services
    databaseMetaData = getDBMetaData();
    schemaTranformation();
}

首先,我有一个模式.graphql具有基本查询和类型的文件,后端需要运行。它导入并运行没有任何错误。然后,它仅出于演示目的获取元数据以尝试从数据库中添加新类型。

public List<GraphQLObjectType> createSchema(String tableName) throws SQLException {
    List<GraphQLObjectType> objectTypes = new ArrayList<>();

    resultSet = databaseMetaData.getColumns(null, null, (String) tableName, null);
    GraphQLObjectType.Builder builder = GraphQLObjectType.newObject();

    while (resultSet.next()) {
        builder
                .name(tableName.toLowerCase())
                .field(GraphQLFieldDefinition.newFieldDefinition()
                        .name(resultSet.getString("COLUMN_NAME"))
                        .type(GraphQLNonNull.nonNull(returnType(resultSet.getString("TYPE_NAME")))))
                .build();
    }

    objectTypes.add(builder.build());
    return objectTypes;
}

使用上述方法,我尝试将新对象类型作为列表返回。之后,我创建了架构转换您将在下面看到的方法,从中获取带有字段的新类型创建模式方法并尝试转换模式。

public void schemaTransformation() throws SQLException {
    resultSet = databaseMetaData.getTables(null, null, null, new String[]{"TABLE"});
    tables = new ArrayList<>();

    while (resultSet.next()) {
        tables.add(resultSet.getString("TABLE_NAME"));
    }

    List<List<GraphQLObjectType>> createdSchemas = new ArrayList<>();
    for (String table : tables) {
        createdSchemas.add(createSchema(table));
    }
    for (List<GraphQLObjectType> graphQLObjectTypeList : createdSchemas) {
        for (GraphQLObjectType graphQLObjectType : graphQLObjectTypeList) {
            GraphQLTypeVisitorStub visitorSub = getVisitorSub(graphQLObjectType);
            this.graphQLSchema = SchemaTransformer.transformSchema(this.graphQLSchema, visitorSub);
        }
    }
}

但是在SchemaTransformer.transformSchema(),它给出了空指针异常。另外,我没有添加this.graphQL = GraphQL.newGraphQL(this.graphQLSchema).build();这段代码,因为我不知道是否需要它。

private GraphQLTypeVisitorStub getVisitorSub(GraphQLObjectType graphQLObjectType) {
    return new GraphQLTypeVisitorStub() {
        @Override
        public TraversalControl visitGraphQLObjectType(GraphQLObjectType objectType,
                                                        TraverserContext<GraphQLSchemaElement> context) {
            GraphQLCodeRegistry.Builder codeRegistryBuilder = context.getVarFromParents(
                    GraphQLCodeRegistry.Builder.class);
            List<GraphQLFieldDefinition> graphQLFieldDefinitions = new ArrayList<>(graphQLObjectType.getFields());
            DataFetcher newDataFetcher = dataFetchingEnvironment -> {
                return userService.healthCheck();
            };

            FieldCoordinates fieldCoordinates = null;
            for (GraphQLFieldDefinition fieldDefinition : graphQLFieldDefinitions) {
                fieldCoordinates = FieldCoordinates.coordinates(
                        objectType.getName(), fieldDefinition.getName());
                codeRegistryBuilder.dataFetcher(fieldCoordinates, newDataFetcher);
            }
            
            return insertAfter(context, graphQLObjectType);
        }
    };
}

此方法返回我需要使用的访问者变换模式方法。

那么,我是在以错误的方式在运行时更改架构,还是依赖项存在错误?也许无法在运行时更改 graphql 模式。我不知道

【问题讨论】:

  • 我认为端点的运行时间变化不好。意外错误的来源。它在未来会产生一些问题。如果多个客户端使用相同的动态端点并且其中一个删除了另一个想要使用的“端点”怎么办?如何同步它们?
  • 如果您要求客户端同步它们,我通过从 /graphql 获取模式类型来解决该问题。您对运行时端点的更改是正确的,但我需要将用户的新表注册为类型,将列注册为字段,以便通过使用 graphql 访问这些数据。主要思想是使 graphql 模式成为访问新数据的动态模式。

标签: java spring spring-boot graphql


【解决方案1】:

@Oguz Tozkoparan,

你设法解决了这个问题吗? 我正在尝试做与您的非常相似的 POC,但我什至不确定这是否可能。

【讨论】:

  • user655419,这篇文章看起来不像是试图回答这个问题。这里的每个帖子都应该是一个明确的尝试回答这个问题;如果你有批评或需要澄清问题或其他答案,你可以直接在它下面post a comment(就像这个)。请删除此答案并创建评论或新问题。请参阅:Ask questions, get answers, no distractions
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-07-27
  • 2017-03-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-06-12
  • 2019-05-30
相关资源
最近更新 更多