数据集成
Spring for GraphQL 让您能够利用现有的 Spring 技术,遵循常见的编程模式,通过 GraphQL 暴露底层数据源。
本节讨论了Spring Data的一个整合层,它提供了一种简单的方法来将Querydsl或示例查询仓库适配到DataFetcher中,包括为标记有@GraphQlRepository的仓库自动检测和注册GraphQL查询选项。
4.1. Querydsl
Spring for GraphQL 支持使用 Querydsl 通过 Spring Data Querydsl 扩展 获取数据。 Querydsl 提供了一种灵活且类型安全的方式来表达查询谓词,通过注解处理器生成元模型。
例如,声明一个仓库(repository)为 QuerydslPredicateExecutor:
public interface AccountRepository extends Repository<Account, Long>,
QuerydslPredicateExecutor<Account> {
}
然后使用它来创建一个DataFetcher:
// For single result queries
DataFetcher<Account> dataFetcher =
QuerydslDataFetcher.builder(repository).single();
// For multi-result queries
DataFetcher<Iterable<Account>> dataFetcher =
QuerydslDataFetcher.builder(repository).many();
您现在可以通过
RuntimeWiringConfigurer注册上述DataFetcher。
The DataFetcher 从 GraphQL 请求参数构建一个 Querydsl Predicate,并使用它来获取数据。Spring Data 支持 QuerydslPredicateExecutor 对应于 JPA、MongoDB 和 LDAP。
如果仓库是ReactiveQuerydslPredicateExecutor,构建器返回DataFetcher<Mono<Account>>或DataFetcher<Flux<Account>>。Spring Data 支持此变体用于MongoDB。
4.1.1. 构建设置
要配置Querydsl,请参阅 官方参考文档:
例如:
dependencies {
//...
annotationProcessor "com.querydsl:querydsl-apt:$querydslVersion:jpa",
'org.hibernate.javax.persistence:hibernate-jpa-2.1-api:1.0.2.Final',
'javax.annotation:javax.annotation-api:1.3.2'
}
compileJava {
options.annotationProcessorPath = configurations.annotationProcessor
}
<dependencies>
<!-- ... -->
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-apt</artifactId>
<version>${querydsl.version}</version>
<classifier>jpa</classifier>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
<artifactId>hibernate-jpa-2.1-api</artifactId>
<version>1.0.2.Final</version>
</dependency>
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
</dependency>
</dependencies>
<plugins>
<!-- Annotation processor configuration -->
<plugin>
<groupId>com.mysema.maven</groupId>
<artifactId>apt-maven-plugin</artifactId>
<version>${apt-maven-plugin.version}</version>
<executions>
<execution>
<goals>
<goal>process</goal>
</goals>
<configuration>
<outputDirectory>target/generated-sources/java</outputDirectory>
<processor>com.querydsl.apt.jpa.JPAAnnotationProcessor</processor>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
The webmvc-http 样例使用了 Querydsl 进行 artifactRepositories.
4.1.2. 自定义配置
QuerydslDataFetcher 支持自定义如何将 GraphQL 参数绑定到属性以创建 Querydsl Predicate。默认情况下,参数会针对每个可用的属性进行“等于”绑定。要自定义这一点,您可以使用 QuerydslDataFetcher 构建器方法来提供一个 QuerydslBinderCustomizer。
一个仓库本身可以是QuerydslBinderCustomizer的一个实例。这会自动检测并在自动注册过程中透明地应用。然而,当你手动构建一个QuerydslDataFetcher时,你需要使用构建器方法来应用它。
QuerydslDataFetcher 支持接口和DTO投影,用于在返回这些结果进行进一步的GraphQL处理之前转换查询结果。
| 要了解投影的相关内容,请参阅 Spring Data 文档。 要理解如何在 GraphQL 中使用投影,请参见 选择集与投影。 |
要使用 Spring Data 投射与 Querydsl 仓库一起工作,请创建一个投射接口或目标 DTO 类,并通过 projectAs 方法进行配置以获取生成目标类型的 DataFetcher:
class Account {
String name, identifier, description;
Person owner;
}
interface AccountProjection {
String getName();
String getIdentifier();
}
// For single result queries
DataFetcher<AccountProjection> dataFetcher =
QuerydslDataFetcher.builder(repository).projectAs(AccountProjection.class).single();
// For multi-result queries
DataFetcher<Iterable<AccountProjection>> dataFetcher =
QuerydslDataFetcher.builder(repository).projectAs(AccountProjection.class).many();
4.1.3. 自动注册
如果仓库类使用了@GraphQlRepository注解,那么它会自动注册用于那些尚未有其他已注册的DataFetcher且返回类型与仓库领域类型匹配的查询。这包括单值和多值查询。
默认情况下,查询返回的GraphQL类型名称必须与仓库领域类型的简单名称匹配。如果需要,可以使用typeName属性来指定目标GraphQL类型名称。
自动注册会检测给定的仓库是否实现了QuerydslBinderCustomizer,并通过QuerydslDataFetcher的构建器方法透明地应用这一实现。
Auto-registration is performed through a built-in RuntimeWiringConfigurer that can be
obtained from QuerydslDataFetcher. The Boot Starter automatically
detects @GraphQlRepository beans and uses them to initialize the
RuntimeWiringConfigurer with.
Auto-registration 应用自定义设置 通过在实现 customize(Builder) 或 QuerydslBuilderCustomizer 接口的仓库实例上调用 ReactiveQuerydslBuilderCustomizer。
4.2. 通过示例查询
Spring Data 支持使用 查询示例(Query by Example) 来获取数据。查询示例(QBE) 是一种简单的查询技术,无需您通过特定于存储的查询语言编写查询。
使用声明一个仓库,该仓库是QueryByExampleExecutor开始的:
public interface AccountRepository extends Repository<Account, Long>,
QueryByExampleExecutor<Account> {
}
使用QueryByExampleDataFetcher将仓库转换为DataFetcher:
// For single result queries
DataFetcher<Account> dataFetcher =
QueryByExampleDataFetcher.builder(repository).single();
// For multi-result queries
DataFetcher<Iterable<Account>> dataFetcher =
QueryByExampleDataFetcher.builder(repository).many();
您现在可以通过
RuntimeWiringConfigurer注册上述DataFetcher。
The DataFetcher 使用 GraphQL 参数映射来创建仓库的域类型,并使用该类型作为查询数据的示例对象。Spring Data 支持QueryByExampleDataFetcher用于 JPA、MongoDB、Neo4j 和 Redis。
如果仓库是ReactiveQueryByExampleExecutor,构建器返回DataFetcher<Mono<Account>>或DataFetcher<Flux<Account>>。Spring Data 支持这种变体用于 MongoDB、Neo4j、Redis 和 R2dbc。
4.2.2. 自定义配置
QueryByExampleDataFetcher 支持接口和DTO投影,在返回这些结果进行进一步的GraphQL处理之前,将查询结果进行转换。
| 要了解什么是投影,请参阅 Spring Data 文档。 要理解投影在 GraphQL 中的作用,请参见 选择集 vs 投影。 |
要使用 Spring Data 投影与 Query by Example 仓库一起工作,可以创建一个投影接口或目标 DTO 类,并通过 projectAs 方法进行配置以获得生成目标类型的 DataFetcher:
class Account {
String name, identifier, description;
Person owner;
}
interface AccountProjection {
String getName();
String getIdentifier();
}
// For single result queries
DataFetcher<AccountProjection> dataFetcher =
QueryByExampleDataFetcher.builder(repository).projectAs(AccountProjection.class).single();
// For multi-result queries
DataFetcher<Iterable<AccountProjection>> dataFetcher =
QueryByExampleDataFetcher.builder(repository).projectAs(AccountProjection.class).many();
4.2.3. 自动注册
如果仓库类使用了@GraphQlRepository注解,那么它会自动注册用于那些尚未有其他已注册的DataFetcher且返回类型与仓库领域类型匹配的查询。这包括单值和多值查询。
默认情况下,查询返回的GraphQL类型名称必须与仓库领域类型的简单名称匹配。如果需要,可以使用typeName属性来指定目标GraphQL类型名称。
Auto-registration is performed through a built-in RuntimeWiringConfigurer that can be
obtained from QueryByExampleDataFetcher. The Boot Starter automatically
detects @GraphQlRepository beans and uses them to initialize the
RuntimeWiringConfigurer with.
自动注册会应用自定义设置,通过在仓库实例上调用customize(Builder)来实现,如果你的仓库实现了QueryByExampleBuilderCustomizer或ReactiveQueryByExampleBuilderCustomizer。
其中:0对应原文中的
4.3. 选择集与投影
一个常见问题是,GraphQL 选择集如何与 Spring Data 投影 比较,并且每种方式各自扮演什么角色?
短答案是,Spring for GraphQL 并不是一个数据网关,它不会直接将GraphQL 查询转换为SQL或JSON查询。相反,它允许你利用现有的Spring技术,并且不假设GraphQL模式与底层数据模型之间有一对一的映射关系。这就是为什么客户端驱动的数据选择和服务器端的数据模型转换可以相互补充。
要更好地理解,请考虑Spring Data推崇领域驱动设计(DDD)作为管理数据层复杂性的推荐方法。在DDD中,遵循聚合的约束非常重要。根据定义,只有当聚合完全加载时才是有效的,因为部分加载的聚合可能会限制聚合的功能。
在Spring Data中,您可以选择是直接暴露聚合结果,还是在将其作为GraphQL结果返回之前对数据模型应用转换。有时只需要前者即可,默认情况下,Querydsl 和 查询示例 整合将GraphQL的选择集转换为Spring Data模块使用的属性路径提示,用于限制选择。
在其他情况下,为了适应GraphQL模式,减少或甚至转换底层数据模型是有用的。Spring Data 通过接口和DTO投影支持这一点。
接口投影定义了一组固定的属性,用于暴露数据,其中的属性可能会或可能不会为null,这取决于数据存储查询结果。有两种类型的接口投影,它们都决定了从底层数据源加载哪些属性。
DTO投影提供了一种更高的自定义级别,因为您可以将转换代码放在构造函数中或getter方法中。
DTO投影是从查询中产生的,其中个别属性由投影本身确定。DTO投影通常与全参数构造函数一起使用(例如Java记录),因此只有当数据库查询结果包含所有必需字段(或列)时,才能构建这些对象。