【发布时间】: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