实体关系

GraphQL查询可以遍历相关对象及其字段,因此只需使用一个查询即可编写如下所示的内容:

{
  books
  {
    items {
      id
      title    
      authors {
        items {
          first_name
          last_name
        }
      }
    }
  }
}

检索书籍及其作者。

若要允许此功能正常工作,数据 API 生成器需要知道这两个对象是如何相互关联的。 relationships配置文件中的 节提供了使此功能正确且高效地工作所需的元数据。

配置关系

无论使用哪个数据库与数据 API 生成器一起使用,必须显式告知数据 API 生成器,一个对象与另一个对象相关。 在两个实体之间可以建立三种类型的关系:

一对多关系

一对多关系允许对象访问相关对象的列表。 例如,书籍系列可以允许访问该系列中的所有书籍:

{
  series {
    items {
      name
      books {
        items {
          title
        }
      }
    }
  }
}

如果有外键支持两个基础数据库对象之间的关系,则只需告诉数据 API 生成器,你希望公开这种关系。 使用 DAB CLI:

dab update Series --relationship books --target.entity Book --cardinality many 

series更新实体 - 在示例中使用:

"Series": {
  "source": "dbo.series",
  ...
  "relationships": {
    "books": {
      "target.entity": "Book",
      "cardinality": "many"    
    }
  }
  ...
}

新键在 元素下 relationships 添加: books。 元素定义用于GraphQL字段的名称,以便从 series 对象导航到 在 中target.entityBook定义的 对象。 这意味着配置文件中必须存在名为 的 Book 实体。

属性cardinality告知数据 API 生成器,每个系列中可以有许多书籍,因此创建的GraphQL字段返回项列表。

该属性就是你所需要的。 启动时,数据 API 生成器会自动检测需要用于维持已定义关系的数据库字段。

如果没有外键约束来维持数据库关系,数据 API 生成器无法自动找出使用了哪些字段。 若要告知数据 API 生成器哪些字段与这两个实体相关,必须手动指定它们。 可以使用 CLI dab update指定它们:

dab update Series --relationship books --target.entity Book --cardinality many  --relationship.fields "id:series_id"

使用 选项 relationship.fields 可以定义 () Series 更新的实体使用哪些字段,以及从目标实体 (Book) 使用哪些字段,以将数据从一个实体连接到另一个实体。

在前面的示例中,id实体的数据库字段Series与实体的数据库字段series_idBook匹配。

配置还包含以下信息:

"Series": {
  "source": "dbo.series",
  ...
  "relationships": {
    "books": {
      "cardinality": "many",
      "target.entity": "Book",
      "source.fields": ["id"],
      "target.fields": ["series_id"]
    }    
  }
  ...
}

多对一关系

多对一关系类似于一对多关系,有两个主要区别:

  • 设置为cardinalityone
  • 创建的 GraphQL 字段返回标量而不是列表

按照之前使用的书籍系列示例,一本书只能位于一个系列中,因此使用以下 DAB CLI 命令创建关系:

dab update Book --relationship series --target.entity Series --cardinality one

生成此配置:

"Book": {
  "source": "dbo.books",
  ...
  "relationships": {       
    "series": {
      "target.entity": "Series",
      "cardinality": "one"
    }
  }
}

这反过来又允许GraphQL查询,如下例所示:

{
  books {
    items {
      id
      title    
      series {
        name
      }
    }
  }
}

每本书还返回其所属的系列。

多对多关系

多对多关系可视为一对多关系和多对一关系协同工作。 一个作者当然可以写多本书 (一对多关系) ,但同样事实是,多个作者可以 (多对一关系) 处理同一本书。

数据 API 生成器本机支持这种类型的关系:

  • 使用一对多/多对一关系。
  • 使用 链接对象

使用一对多/多对一关系

一个业务要求,可能是跟踪版税是如何在书籍作者之间分配的。 若要实现此类要求,需要一个将作者、书籍和分配的版税链接在一起的专用实体。 因此,需要三个实体:

  • authors,表示作者的简历详细信息。
  • books,表示书籍数据,如标题和国际标准书号 (ISBN) 。
  • books_authors 表示与书籍及其作者相关的数据,例如,作者为特定书籍获得的版税百分比。

可以通过下图可视化这三个实体。

显示作者、books_authors和书籍之间多对多关系的示意图。

可见,有两个双向关系:

  • 与 之间的 authors 一对多/多对一关系 books_authors
  • 与 之间的 books 一对多/多对一关系 books_authors

若要正常使用 DAB 处理此类方案,只需在配置文件中创建相关实体和映射即可。 假设 BookAuthor 实体已在配置文件中:

dab add BookAuthor --source dbo.books_authors --permissions "anonymous:*"

若要添加新实体,请运行 dab update

dab update Book --relationship authors --target.entity BookAuthor --cardinality many --relationship.fields "id:book_id"
dab update Author --relationship books --target.entity BookAuthor --cardinality many --relationship.fields "id:author_id"

若要将关系添加到新创建的 BookAuthor 实体,请再次运行 dab update

dab update BookAuthor --relationship book --target.entity Book --cardinality one --relationship.fields "book_id:id"
dab update BookAuthor --relationship author --target.entity Author --cardinality one --relationship.fields "author_id:id"

将 关系从 BookAuthorBook 添加到 和 Author 实体。 使用提供的配置,DAB 能够处理嵌套查询,如以下示例所示:

{
 authors {
    items {
      first_name
      last_name      
      books {
        items {
          book {
            id
            title
          }
          royalties_percentage
        }
      }      
    }
  }
}

当你要求归还所有作者的地方,他们写的书以及相关的版税。

使用链接对象

如果需要通过GraphQL访问多对多关系中涉及的所有实体,则上一部分所述的过程非常有效。 这种情况并非总是如此。 例如,如果不需要跟踪版税,实体 BookAuthor 不会真正为最终用户带来任何价值。 该实体仅用于将书籍与其作者相关联。 在关系数据库中,多对多关系是使用这样的第三个表创建的,该表将参与多对多关系的表 链接 在一起:

显示作者、books_authors和书籍之间另一个多对多关系的示意图。

在关系图中,可以看到有一个名为 books_authors 的表,该表将作者与其书籍和书籍与其作者链接在一起。 此链接表不需要向最终用户公开。 链接表只是允许多对多关系存在的项目,但数据 API 生成器需要知道其存在才能正确使用它。

DAB CLI 可用于创建多对多关系,并配置链接对象 (确保删除在上一部分中创建的所有关系,并且仅从 BookAuthor 实体开始,并且它们之间已) 没有配置的关系:

dab update Book --relationship authors --target.entity Author --cardinality many --relationship.fields "id:id" --linking.object "dbo.books_authors" --linking.source.fields "book_id" --linking.target.fields "author_id" 

它更新 JSON 配置文件,如下所示:

"Book": {
  "source": "dbo.books",
  ...
  "relationships": {       
    "authors": {
      "cardinality": "many",
      "target.entity": "author",
      "source.fields": [ "id" ],
      "target.fields": [ "id" ],
      "linking.object": "dbo.books_authors",
      "linking.source.fields": [ "book_id" ],
      "linking.target.fields": [ "author_id" ]
    }
  }
}

该配置告知 DAB 你要在Book实体中添加一个authors允许访问书籍作者的字段。 authors可以是 many,因此当GraphQL查询访问authors字段时,将返回作者列表。 此关系定义如何 书籍 导航到 作者:用于从书籍导航到作者的数据库字段在 书籍的 中 source.fields 定义,在 作者的 中 target.fields 定义,类似于本文前面所述的一对多或多对一关系。

此关系是多对多关系,因此两个实体之间没有直接连接,因此 linking.object 需要使用 。 在示例中,数据库表 dbo.books_authors 用作链接对象。 链接对象如何能够将书籍连接到其作者在 和 linking.target.fields 属性中linking.source.fields定义。 第一个命令告诉 DAB 源实体 ( - - Book 如何连接到喜欢对象),第二个告知链接对象如何连接到示例中的目标实体 Author

若要了解如何使用提供的信息,可以使用以下示例等效查询:

select * 
from dbo.books as b
inner join dbo.books_authors as ba on b.id = ba.book_id 
inner join dbo.authors a on ba.author_id = a.id 

使用提供的配置,DAB 能够理解GraphQL,如以下示例所示:

{
  books {
    items {
      id
      title
      authors {
        items {
          first_name
          last_name
        }
      }
    }
  }
}

你想在哪里获取书籍及其作者。

若要允许从 Author 导航到 Book,可以应用相同的原则,使用以下命令更新配置:

dab update Author --relationship books --target.entity Book --cardinality many --relationship.fields "id:id" --linking.object "dbo.books_authors" --linking.source.fields "author_id" --linking.target.fields "book_id" 

它定义实体与实体之间的Author多对多关系,在后台使用链接对象dbo.books_authorsBook