---
title: Flutter 中 Parse SDK 实现多对多关系的详细指南
slug: docs-ch/flutter/parse-sdk/data-objects/flutter-many-to-many-relationship
description: 在 Flutter 中使用 Parse SDK 创建和查询多对多关系的技术实现。探讨 Parse 的三种常用方法以实现高效关系管理。
image: https://www.back4app.com/images/logo-social.png
createdAt: 2024-03-15T15:45:12.640Z
---

# Flutter中的多对多关系

## 介绍

在之前的指南中，我们学习了如何使用一对多关系，我们将继续我们的项目。在本指南中，我们将重点关注最常见的关系：多对多关系。在Parse中有三种方法可以创建多对多关系。

- 第一种是使用Parse <font color="#2166ae">`关系`</font>, 这是创建和查询时间最快的方法。我们将在本指南中使用此方法。
- 第二种是使用<font color="#2166ae">`数组`</font>的 <font color="#2166ae">`指针`</font>，这可能会导致查询时间变慢，具体取决于它们的大小。
- 第三种是使用<font color="#2166ae">`连接表`</font>，这是经典数据库中的一个概念。当存在多对多关系时，我们将每个 <font color="#2166ae">`objectId`</font>或 <font color="#2166ae">`指针`</font>从两边组合在一起，以构建一个新的单独表格，在其中跟踪关系。

在本指南中，您将实现一个 <font color="#2166ae">`多对多`</font> 关系，在一个Flutter图书注册应用程序中使用Parse <font color="#2166ae">`关系`</font>. 您将学习如何创建和查询多对多数据关系，以及如何执行查询以返回来自相关对象的数据，使用Back4App和Flutter SDK。

::embed[]{url="https://www.youtube.com/embed/Vfyo-g8vjPs"}

## 前提条件

:::hint{type="info"}
- [Android Studio ](https://developer.android.com/studio)或 [安装 VS Code](https://code.visualstudio.com/)（带有 [插件](https://docs.flutter.dev/get-started/editor) Dart 和 Flutter）
- **注意：在之前的指南中创建的 Flutter 应用**
- 完成之前的指南，以便更好地理解 <font color="#2166ae">`一对多关系`</font> 类
- 一台运行 Android 或 iOS 的设备（或虚拟设备）。
:::

## 1 - 运行图书应用模板

如果您尚未完成之前的指南，您可以从我们的代码库中克隆并运行完整的 [图书 Flutter 应用](https://github.com/templates-back4app/flutter_associations) 项目。您还可以查看 [之前的指南](https://www.back4app.com/docs/flutter/parse-sdk/data-objects/%7Bsite.baseurl%7D%7D//flutter/parse-sdk/data-objects/flutter-one-to-many-relationship) 以更好地理解应用模板。下面您可以找到图书注册数据模型的可视化表示。

![](https://api.archbee.com/api/optimize/yD3zCY-NNBBIfd0uqcfR5/Swe2-7FvMIocPiAXjY-N-_image.png)

## 2 - 保存一本书对象及其作者

打开之前指南中的 Flutter 项目 [Flutter 中的一对多关系](https://www.back4app.com/docs//flutter/parse-sdk/data-objects/flutter-one-to-many-relationship)。搜索文件中的函数 <font color="#2166ae">`doSaveBook`</font> ，并在 <font color="#2166ae">`main.dart`</font> 文件中用下面的代码替换它。这个函数将在 Back4app 数据存储中创建一个新的书籍对象，并建立关系。

```dart
1       final book = ParseObject('Book')
2         ..set('title', controllerTitle.text.trim())
3         ..set('year', int.parse(controllerYear.text.trim()))
4         //the objectId will be converted to a Pointer on the save() method
5         ..set('genre', ParseObject('Genre')..objectId = genre.objectId)
6         //you can also convert to a Pointer object before the saving using the .toPointer() method
7         ..set('publisher',
8             (ParseObject('Publisher')..objectId = publisher.objectId).toPointer())
9         //Saving a List of Authors for the Book
10        ..addRelation(
11            'authors',
12            authors
13                .map((o) => ParseObject('Author')..objectId = o.objectId)
14                .toList());
15 
16      await book.save();
```

要构建此函数，请按照以下步骤操作：

- 1\. 创建一个新的 Parse 实例<font color="#2166ae">`书籍`</font> 类，使用命令 <font color="#2166ae">`ParseObject('书籍')`</font>.
- 2\. 使用 <font color="#2166ae">`设置`</font> 函数来设置此对象的字段。
  - 2.1.<font color="#2166ae">`标题`</font>是一个文本属性，从文本控制器接收值。
  - 2.2.<font color="#2166ae">`类型`</font> 通过定义一个 <font color="#2166ae">`ParseObject`</font> 与 <font color="#2166ae">`objectId`</font> 的选定 <font color="#2166ae">`类型`</font>. (*Parse将在保存时转换为指针*)
  - 2.3.<font color="#2166ae">`发布者`</font> 通过定义一个 <font color="#2166ae">`ParseObject`</font> 来接收值，<font color="#2166ae">`objectId`</font> 的选定 <font color="#2166ae">`发布者`</font>. (*请注意，我们可以为 Parse 指定我们想要保存为一个&#x20;*<font color="#2166ae">`指针`</font>*&#x20;使用&#x20;*<font color="#2166ae">`toPointer()`</font>*&#x20;方法*)
  - 2.4.<font color="#2166ae">`作者`</font>. 我们称之为 <font color="#2166ae">`addRelation`</font> 方法的 <font color="#2166ae">`ParseObject`</font>, 发送一个 <font color="#2166ae">`ParseObject`</font> 的列表，带有 <font color="#2166ae">`objectId`</font> 的选定 <font color="#2166ae">`作者`</font>.
- 3\. 调用<font color="#2166ae">`保存`</font> 函数在 <font color="#2166ae">`ParseObject`</font>, 这将有效地将对象注册到您的数据库中，在 Back4app 控制面板中.

运行应用程序并测试新的 <font color="#2166ae">`doSaveBook()`</font> 函数

- 首先，访问仪表板并删除在之前指南中注册的书籍。
- 点击 <font color="#2166ae">`添加书籍`</font> 按钮。
- 填写书籍信息和作者。
- 点击 <font color="#2166ae">`保存书籍`</font> 按钮

::Image[]{src="https://api.archbee.com/api/optimize/yD3zCY-NNBBIfd0uqcfR5/O0kajxCDCm8k3Q2aBeI_u_image.png" size="30" width="323" height="627" position="center" showCaption="false"}

要确认新对象是否与关系一起保存在数据库中，您可以访问 <font color="#2166ae">`Back4app Dashboard`</font> 并访问 <font color="#2166ae">`Book`</font> 类。

::Image[]{src="https://api.archbee.com/api/optimize/yD3zCY-NNBBIfd0uqcfR5/lE7tRKvGzi1YioqFBkxZb_image.png" size="30" width="323" height="627" position="center" showCaption="false"}

在您的仪表板中点击对象指针/关系值将带您到引用的对象条目。这看起来可能是一个无害的功能，但这使得调试和错误追踪比手动搜索快得多。

## 3 - 查询带有关系的书籍详情

此功能将在 Back4app 数据库中查询书籍详情，返回关系数据。在某些情况下，您可能希望在一个查询中返回多种类型的相关对象。您可以使用 <font color="#2166ae">`includeObject`</font> 方法来实现。在我们的示例中，我们希望返回书籍，并包含来自类型和出版商的信息。

在文件中搜索函数 <font color="#2166ae">`getBookDetail`</font> ，然后在 <font color="#2166ae">`main.dart`</font> 文件中替换以下代码 <font color="#2166ae">`getBookDetail(ParseObject book)`</font> 函数:

```dart
1       QueryBuilder<ParseObject> queryBook =
2           QueryBuilder<ParseObject>(ParseObject('Book'))
3             ..whereEqualTo('objectId', book.objectId)
4             ..includeObject(['publisher', 'genre']);
5   
6       final ParseResponse responseBook = await queryBook.query();
7
8       if (responseBook.success && responseBook.results != null) {
9         final book = (responseBook.results.first) as ParseObject;
10        bookTitle = book.get<String>('title');
11        bookYear = book.get<int>('year');
12        bookGenre = book.get<ParseObject>('genre').get<String>('name');
13        bookPublisher = book.get<ParseObject>('publisher').get<String>('name');
14        loadedData = true;
15      }
16
17      QueryBuilder<ParseObject> queryAuthors =
18          QueryBuilder<ParseObject>(ParseObject('Author'))
19            ..whereRelatedTo('authors', 'Book', book.objectId);
20
21      final ParseResponse responseAuthors = await queryAuthors.query();
22
23      if (responseAuthors.success && responseAuthors.results != null) {
24        bookAuthors = responseAuthors.results
25            .map((e) => (e as ParseObject).get<String>('name'))
26            .toList();
27      }
```

要构建此函数，请按照以下步骤操作:

1. 创建一个<font color="#2166ae">`ParseQuery`</font>对象的实例，用于 <font color="#2166ae">`Book`</font> 类。插入一个条件到查询中，以搜索 <font color="#2166ae">`Books`</font>，其中 <font color="#2166ae">`objectId`</font> 字段等于所选书籍的 <font color="#2166ae">`objectId`</font>。
2. 我们使用 includeObject 方法，告知我们希望在同一查询中返回的指针字段：类型和出版商。您还可以使用点表示法进行多级 includeObject。示例：\`..includeObject(\[‘post’, ‘post.authors’]);
3. 使用 <font color="#2166ae">`query()`</font> 方法进行查询的搜索。
4. 如果操作成功，将返回 <font color="#2166ae">`Book`</font> 中的对象。
5. 我们使用 get 方法来检索数据。对于指针字段，我们首先需要检索指针，然后获取其数据。示例：<font color="#2166ae">`bookGenre = book.get<ParseObject>('genre').get<String>('name');`</font>

在处理的第二阶段，我们需要恢复与书籍相关的作者。

要构建此功能，请按照以下步骤操作：

1. 为 <font color="#2166ae">`ParseQuery`</font> 类创建一个 <font color="#2166ae">`Authors`</font> 对象。在查询中插入一个条件，使用 <font color="#2166ae">`whereRelatedTo`</font> 操作符来搜索 <font color="#2166ae">`Authors`</font> 与 <font color="#2166ae">`Book`</font>, 其中 <font color="#2166ae">`Book`</font> 等于所选书籍的 <font color="#2166ae">`objectId`</font>。
2. 使用 <font color="#2166ae">`query()`</font> 方法进行查询的搜索。
3. 如果操作成功，将返回 <font color="#2166ae">`Book`</font> 中的对象。
4. 我们使用 get 方法来检索数据。

运行应用程序并测试新的查询。

首先，点击 <font color="#2166ae">`列表出版商/书籍`</font> 按钮。

::Image[]{src="https://api.archbee.com/api/optimize/yD3zCY-NNBBIfd0uqcfR5/MtZZXEKd2vUJ7afLiv-wR_image.png" size="30" width="323" height="627" position="center" showCaption="false"}

从列表中选择一本书。下一个屏幕将显示书籍及其关系的数据。

::Image[]{src="https://api.archbee.com/api/optimize/yD3zCY-NNBBIfd0uqcfR5/IkAfHVHlwA-8sTmhK7cxh_image.png" size="30" width="328" height="637" position="center" showCaption="false"}

## 完成！

此时，您已学习如何创建和查询多对多关系，以及如何在 <font color="#2166ae">`Flutter`</font> 中执行返回相关对象数据的查询。
