Spring Boot和Netflix DGS的GraphQL源码案例


Netflix Domain Graph Service(DGS)是在Netflix内部创建的一个新的开放源代码框架,该框架简化并帮助了使用GraphQL实施Spring Boot应用程序。
DGS的特征:

  • 它是一个基于Spring Boot样式的注释的系统。
  • 与Spring Security集成。
  • 错误管理。
  • 适用于Java的GraphQL客户端。
  • 支持WebSockets,SSE,文件上传或GraphQL Federation。

Netflix的域图服务(DGS)框架极大地促进了带有Spring Boot的GraphQL的开发,从而能够以更简单的方式构建任何API。由于GraphQL的适应性和灵活性以及其他特性,它被定位为Rest的广泛使用的替代方法,因此使用GraphQL的频率越来越高。
 
案例说明
完整的示例在github上
这个示例中,将使用带有H2的内存关系数据库,以简化使用JPA的示例。我们的示例将由三个非常基本的实体组成:银行,用户和帐户银行,其中用户在银行中有一个或N个银行帐户,而一个银行有1个或N个银行帐户。
在我们的应用程序中,使用Lombok简化删除Java样板。
 
添加依赖:
<dependency>
   <groupId>com.netflix.graphql.dgs</groupId>
   <artifactId>graphql-dgs-spring-boot-starter</artifactId>
   <version>${netflix-dgs.version}</version>
</dependency>

有必要为我们的应用程序添加更多的依赖项。
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
  <groupId>com.h2database</groupId>
  <artifactId>h2</artifactId>
  <scope>runtime</scope>
</dependency>

 
创建GraphQL的Schema
与创建关系数据库的表结构一样,我们将对GraphQL进行相同的操作,即创建schema。我们创建的必须位于以下路径/ src / main / resources / schema中,并且在此路径中,Netflix DGS将负责加载模式。
对于每个模式,我们创建了一个扩展名为graphqls的不同文件。其中有一个QueryResolver类型,它将具有两个不同的搜索,以及一个MutationResolver以便能够在数据库中创建对象。
创建3个不同的方案,帐户,用户和银行:
type QueryResolver {
    users: [User]
    user(id: ID!): User!
}

type MutationResolver {
    createUser(user: UserInput!): User
}

input UserInput {
    firstName: String!
    lastName: String!
    address: String!
    country: String!
    city: String!
    age: Int
}

type User {
    id: ID!
    firstName: String!
    lastName: String!
    address: String!
    country: String!
    city: String!
    age: Int!
    bank: Bank
    accounts: [Account]
}

schema {
    query: QueryResolver
    mutation: MutationResolver
}

  
在Spring Boot中创建实体
接下来,我们将创建负责映射关系数据库和GraphQL模式的实体。
@Entity
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
public class User {

    @Id
    @GeneratedValue
    private UUID id;

    private String firstName;

    private String lastName;

    private int age;

    private String address;

    private String country;

    private String city;

    @OneToMany(mappedBy = "user")
    private Set<Account> accounts;

    @ManyToOne()
    private Bank bank;
}

 
创建变化对象
如前所述,我们已经为每个架构创建了一个MutationResolver类型,因此我们需要创建一个新的对象,并传递该对象来创建它。也就是说,它将是与之进行绑定的对象,当进行请求和调用时,它将具有与我们在模式中输入的名称相同的名称。
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
public class UserInput {

    private String firstName;

    private String lastName;

    private int age;

    private String address;

    private String country;

    private String city;
}

  
使用Spring Boot创建存储库
对于持久性或存储库层,我们将使用JpaRepository,它将为我们提供所有JPA工具,使其能够保存和查询我们的数据库对象。
例如,对于“帐户”,如下所示:
public interface AccountRepository extends JpaRepository<Account, UUID> {
}

 
Spring Boot的Netflix DGS
Netflix已经创建了它的基于注释的DGS框架,该框架由Spring Boot支持。下面我们展示了要使应用程序与GrpahQL和Spring Boot一起运行时要考虑的基本注释。
  • @DgsComponent:此批注负责指示类将要执行查询。该类应在定义中带有此注释。
  • @DgsData,每个带有逻辑以执行查询的方法都必须使用@DgsData进行注释。我们将在其中添加父类型的位置,这是架构的类型以及在架构中定义的字段。
  • @InputArgument,用于定义在查询中作为参数传递的参数。
  • @DgsQuery,是parentType为query时@DgsData的缩写。
  • @DgsMutation,是parentType为Mutation时@DgsData的缩写。

使用DGS时要记住的另一个问题是能够通过实现DgsCustomContextBuilder类来创建自己的上下文。您可以使用它来记录或存储状态,或保存一些通过DataFetchingEnvironment类访问的信息。在示例类UserQuery中,您可以看到其用法。
 
在Spring Boot中使用Netflix DGS查询
Netflix DGS的查询类使用了我们上面提到的一些注释:
@DgsComponent
@RequiredArgsConstructor
public class UserQuery {

    private final UserRepository userRepository;

    private final CustomContextBuilder contextBuilder;


    @DgsData(parentType = "QueryResolver", field = "users")
    public Iterable<User> findAll(DgsDataFetchingEnvironment dfe) {

        var users = (List<User>) userRepository.findAll();

        contextBuilder.customContext(users, null, null).build();

        return users;
    }

    @DgsData(parentType = "QueryResolver", field = "user")
    public User findById(@InputArgument("id") String id, DataFetchingEnvironment dfe) {

        CustomContext customContext = DgsContext.getCustomContext(dfe);

        var users = customContext.getUsers();


        if (null != users) {
            var user = users.stream().filter(u -> u.getId().equals(UUID.fromString(id))).findFirst();

            return user.orElseGet(() -> userRepository.findById(UUID.fromString(id)).orElseThrow(DgsEntityNotFoundException::new));

        } else {

            return userRepository.findById(UUID.fromString(id)).orElseThrow(DgsEntityNotFoundException::new);
        }

    }
}

该类的一个亮点是CustomContextBuilder的使用,该类创建了自己的上下文,在该上下文中添加了不同的返回对象,然后可以直接对其进行访问。
 

在Spring Boot中使用Netflix DGS进行更改
接下来,我们将看到UserMutation类,该类负责在数据库中保存新用户。
如您在@DgsData中所看到的,我们首先指示parentType是MutationResolver,字段是createUser,它与模式中的定义匹配。

@DgsComponent
@RequiredArgsConstructor
public class UserMutation {

    private final UserRepository userRepository;

    @DgsData(parentType = "MutationResolver", field = "createUser")
    public User createUser(@InputArgument("user") UserInput user) {
        return userRepository.save(new User(null, user.getFirstName(), user.getLastName(), user.getAge(), user.getAddress(), user.getCountry(), user.getCity(), null, null));
    }

}
 

使用Spring Boot测试Netflix DGS应用程序
完成应用程序并全部实现后,就可以启动一些查询了。
我们使用以下命令运行我们的应用程序:mvn spring-boot:run
我们访问页面 http://localhost:8080/graphiql,然后会出现一个界面,我们可以在其中启动查询。