---
title: Flutter 关系数据查询教程：使用Parse进行复杂查询
slug: docs-ch/flutter/parse-sdk/data-objects/flutter-query-relational
description: 了解如何在Flutter应用中使用Parse SDK进行关系数据查询，使用QueryBuilder简化复杂查询。适合软件开发者的技术指南。
image: https://www.back4app.com/images/logo-social.png
createdAt: 2024-03-18T12:08:53.051Z
---

# 在Flutter中解析关系查询

## 介绍

您已经看到 [如何存储关系数据对象](https://www.back4app.com/docs/flutter/parse-sdk/data-objects/flutter-one-to-many-relationship) 使用Parse.Pointer和Parse.Relations数据类型。您已经了解到在Parse中，可以将任何ParseObject作为其他ParseObject中的值，从而建立它们之间的关系。在内部，Parse框架将把被引用的对象存储在一个地方以保持一致性。这可以在构建和运行复杂查询时为您提供额外的能力。

此外，您已经 [学习了如何使用QueryBuilder](https://www.back4app.com/docs/flutter/parse-sdk/data-objects/flutter-query)，使用get可以从Back4App中检索单个ParseObject。还有许多其他方法可以使用QueryBuilder检索数据。

在本指南中，您将深入研究QueryBuilder类，并查看可以用来构建关系查询的方法。您将使用一个简单的数据库类和一些模拟数据来使用Flutter在Back4App上执行查询。

## 先决条件

:::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）
- 一个在 Back4App 上 [创建的应用](https://www.back4app.com/docs/get-started/new-parse-app)：
  - **注意：**&#x8BF7;遵循 [新 Parse 应用教程](https://www.back4app.com/docs/get-started/new-parse-app) 以了解如何在 Back4App 上创建 Parse 应用。
- 一个连接到 Back4app 的 Flutter 应用。
  - **注意：**&#x8BF7;遵循 [在 Flutter 项目中安装 Parse SDK](https://www.back4app.com/docs/flutter/parse-sdk/parse-flutter-sdk) 以创建一个连接到 Back4App 的 Flutter 项目。
- 一台运行 Android 或 iOS 的设备（或虚拟设备）。
:::

## 目标

从 Flutter 应用查询存储在 Back4App 上的关系数据。

## 1 - 理解 QueryBuilder 类

任何 Parse 查询操作都使用 QueryBuilder 对象类型，它将帮助您在应用程序中从数据库中检索特定数据。

要创建一个新的 QueryBuilder，您需要将所需的 ParseObject 子类作为参数传递，该子类将包含您的查询结果。

了解 QueryBuilder 仅在调用检索方法查询后才会解析是至关重要的，因此可以设置查询并在实际调用之前链接多个修饰符。

```dart
1	       // This will create your query
2      QueryBuilder<ParseObject> queryBook =
3           QueryBuilder<ParseObject>(ParseObject('Book'));
4        
5       	// The query will resolve only after calling this method
6      final ParseResponse apiResponse = await queryBook.query();
7	
8       if (apiResponse.success && apiResponse.results != null) {
9         ....
10      } else {
11  	   ...
12      }
```

你可以在官方文档中阅读更多关于 QueryBuilder 类的内容 [这里](https://github.com/parse-community/Parse-SDK-Flutter/tree/master/packages/flutter#complex-queries).

## 在 Back4App 上使用 JavaScript 控制台

在你的 Back4App 应用程序的仪表板中，你会找到一个非常有用的 API 控制台，在其中你可以直接运行 JavaScript 代码。在本指南中，你将使用它在 Back4App 中存储数据对象。在你的应用主仪表板中，转到 Core->API 控制台->Javascript。

![](https://api.archbee.com/api/optimize/yD3zCY-NNBBIfd0uqcfR5/uAoF6bUPQ86DeL5ddVeH-_image.png)

## 2 - 在 Back4app 上保存数据

要运行本指南中的查询，您首先需要为您的应用填充一些数据。

类包括： `作者`, `书籍`, `出版商` 和 `书店`.
其中 `书籍` 与 `出版商` 有 `1:N` 关系，与 `作者` 有 `N:N` 关系，而 `书店` 与书籍有 `N:N` 关系。

这里是 `Parse.Object` 类的创建代码，所以请在您的 API 控制台中运行它:

```dart
1   // Add objects and create tables
2   // Authors
3   const AuthorA = new Parse.Object('Author');
4   AuthorA.set('name', 'Aaron Writer');
5   await AuthorA.save();
6
7   const AuthorB = new Parse.Object('Author');
8   AuthorB.set('name', 'Beatrice Novelist');
9   await AuthorB.save();
10
11  const AuthorC = new Parse.Object('Author');
12  AuthorC.set('name', 'Casey Columnist');
13  await AuthorC.save();
14
15  const AuthorD = new Parse.Object('Author');
16  AuthorD.set('name', 'Gary Stur');
17  await AuthorD.save();
18
19  const AuthorE = new Parse.Object('Author');
20  AuthorE.set('name', 'Mary Sue');
21  await AuthorE.save();
22
23  // Publishers
24  const PublisherA = new Parse.Object('Publisher');
25  PublisherA.set('name', 'Acacia Publishings');
26  await PublisherA.save();
27
28  const PublisherB = new Parse.Object('Publisher');
29  PublisherB.set('name', 'Birch Distributions');
30  await PublisherB.save();
31  
32  const PublisherC = new Parse.Object('Publisher');
33  PublisherC.set('name', 'Acacia Distributions');
34  await PublisherC.save();
35
36
37  // Books
38  const BookA = new Parse.Object('Book');
39  BookA.set('title', 'A Love Story');
40  BookA.set('publisher', PublisherA);
41  BookA.set('publishingDate', new Date('05/07/1998'));
42  const BookARelation = BookA.relation("authors");
43  BookARelation.add(AuthorA);
44  await BookA.save();
45 
46  const BookB = new Parse.Object('Book');
47  BookB.set('title', 'Benevolent Elves');
48  BookB.set('publisher', PublisherB);
49  BookB.set('publishingDate', new Date('11/31/2008'));
50  const BookBRelation = BookB.relation("authors");
51  BookBRelation.add(AuthorB);
52  await BookB.save();
53 
54  const BookC = new Parse.Object('Book');
55  BookC.set('title', 'Can You Believe It?');
56  BookC.set('publisher', PublisherB);
57  BookC.set('publishingDate', new Date('08/21/2018'));
58  const BookCRelation = BookC.relation("authors");
59  BookCRelation.add(AuthorA);
60  BookCRelation.add(AuthorC);
61  await BookC.save();
62
63  // BookStore
64  const BookStoreA = new Parse.Object('BookStore');
65  BookStoreA.set('name', 'Books of Love');
66  const BookStoreARelation = BookStoreA.relation("books");
67  BookStoreARelation.add(BookA);
68  await BookStoreA.save();
69  
70  const BookStoreB = new Parse.Object('BookStore');
71  BookStoreB.set('name', 'Fantasy Books');
72  const BookStoreBRelation = BookStoreB.relation("books");
73  BookStoreBRelation.add(BookB);
74  await BookStoreB.save();
75  
76  const BookStoreC = new Parse.Object('BookStore');
77  BookStoreC.set('name', 'General Books');
78  const BookStoreCRelation = BookStoreC.relation("books");
79  BookStoreCRelation.add(BookA);
80  BookStoreCRelation.add(BookC);
81  await BookStoreC.save();
82 
83  console.log('Success');
```

运行此代码后，您现在应该在数据库中拥有 Author、Publisher、Book 和 BookStore 类。

您的新类应该看起来像这样:

![](https://api.archbee.com/api/optimize/yD3zCY-NNBBIfd0uqcfR5/8xnDcV6IaJ9WNPPw9ZiSY_image.png)

现在让我们看看每个 QueryBuilder 方法的示例，以及它们的简要说明。

## 3 - 查询数据

现在您已经有了一个填充的类，我们可以执行一些关系查询。

现在您已经填充了所有类，我们可以在其中执行一些关系查询。

让我们开始通过出版商过滤书籍结果，搜索属于出版商 *“Acacia Publishings”*（或“出版商A”）的书籍，使用基本的 .whereEqualTo 方法进行关系指针查询:

```dart
1       // Get PublisherA object
2       final QueryBuilder<ParseObject> publisherQueryA =
3           QueryBuilder<ParseObject>(ParseObject('Publisher'))
4             ..whereEqualTo('name', 'Acacia Publishings');
5   
6       final ParseResponse publisherResponse = await publisherQueryA.query();
7       if (!publisherResponse.success) {
8         return;
9       }
10      final publisherA = publisherResponse.results?.first as ParseObject;
11
12      // Query Books with PublisherA
13      final QueryBuilder<ParseObject> bookQuery =
14          QueryBuilder<ParseObject>(ParseObject('Book'))
15            ..whereEqualTo('publisher', publisherA);
16
17      final ParseResponse bookResponse = await bookQuery.query();
18      if (!bookResponse.success) {
19        return;
20      }
21  
22      for (var book in bookResponse.results as List<ParseObject>) {
23        print(book.get('title'));
24      }
```

现在让我们查询哪些书店对象包含出版日期晚于 2010 年 1 月 1 日的书籍，使用内部查询与 whereGreaterThan 方法，然后使用 whereMatchesQuery 方法:

```dart
1   // Create inner Book query
2       final QueryBuilder<ParseObject> bookQuery =
3           QueryBuilder<ParseObject>(ParseObject('Book'))
4             ..whereGreaterThan('publishingDate', DateTime(2010, 01, 01));
5
6       // Query BookStore using inner Book query
7       final QueryBuilder<ParseObject> bookStoreQuery =
8           QueryBuilder<ParseObject>(ParseObject('BookStore'))
9             ..whereMatchesQuery('books', bookQuery);
10  
11      final ParseResponse bookStoreResponse = await bookStoreQuery.query();
12      if (!bookStoreResponse.success) {
13        return;
14      }
15
16      for (var b in bookStoreResponse.results as List<ParseObject>) {
17        print(b.get('name'));
18      }
```

现在让我们创建另一个查询，寻找与书籍 **Can You Believe It** 相关的作者，使用 whereRelatedTo:

```dart
1       // Get Book object
2       final QueryBuilder<ParseObject> bookQuery =
3           QueryBuilder<ParseObject>(ParseObject('Book'))
4             ..whereEqualTo('title', 'Can You Believe It?');
5
6       final ParseResponse bookResponse = await bookQuery.query();
7       if (!bookResponse.success) {
8         return;
9       }
10
11      final book = bookResponse.results?.first as ParseObject;
12
13      // Get Author with relation with Book Can You Believe It?
14      final QueryBuilder<ParseObject> authorsQuery =
15          QueryBuilder<ParseObject>(ParseObject('Author'))
16            ..whereRelatedTo('authors', 'Book', book.objectId!);
17
18      final ParseResponse authorResponse = await authorsQuery.query();
19
20      // Let's show the results
21      if (authorResponse.success && authorResponse.results != null) {
22        for (var a in authorResponse.results! as List<ParseObject>) {
23          print(a.get('name'));
24        }
25      }
```

## 4 - 从 Flutter 查询

现在让我们在 Flutter 应用中使用我们的示例查询，界面简单，包含一个显示结果的列表和三个用于调用查询的按钮。

打开你的 Flutter 项目，进入 main.dart 文件，清理所有代码，并用以下内容替换它：

```dart
1   import 'package:flutter/cupertino.dart';
2   import 'package:flutter/material.dart';
3   import 'package:parse_server_sdk_flutter/parse_server_sdk.dart';
4
5   void main() async {
6     WidgetsFlutterBinding.ensureInitialized();
7
8     final keyApplicationId = 'YOUR_APP_ID_HERE';
9     final keyClientKey = 'YOUR_CLIENT_KEY_HERE';
10    final keyParseServerUrl = 'https://parseapi.back4app.com';
11
12    await Parse().initialize(keyApplicationId, keyParseServerUrl,
13        clientKey: keyClientKey, debug: true);
14
15    runApp(MaterialApp(
16      title: 'Flutter - GeoPoint',
17      debugShowCheckedModeBanner: false,
18      home: HomePage(),
19    ));
20  }
21
22  class HomePage extends StatefulWidget {
23    @override
24    _HomePageState createState() => _HomePageState();
25  }
26 
27  class _HomePageState extends State<HomePage> {
28    List<ParseObject> results = <ParseObject>[];
29    double selectedDistance = 3000;
30  
31    void doQueryPointer() async {
32      // Get PublisherA object
33      final QueryBuilder<ParseObject> publisherQueryA =
34          QueryBuilder<ParseObject>(ParseObject('Publisher'))
35            ..whereEqualTo('name', 'Acacia Publishings');
36
37      final ParseResponse publisherResponse = await publisherQueryA.query();
38      if (!publisherResponse.success) {
39        return;
40      }
41      final publisherA = publisherResponse.results?.first as ParseObject;
42  
43      // Query Books with PublisherA
44      final QueryBuilder<ParseObject> bookQuery =
45          QueryBuilder<ParseObject>(ParseObject('Book'))
46            ..whereEqualTo('publisher', publisherA);
47  
48      final ParseResponse bookResponse = await bookQuery.query();
49  
50      if (!bookResponse.success) {
51        setState(() {
52          results.clear();
53        });
54      } else {
55        setState(() {
56          results = bookResponse.results as List<ParseObject>;
57        });
58      }
59    }
60
61    void doQueryMatches() async {
62      // Create inner Book query
63      final QueryBuilder<ParseObject> bookQuery =
64          QueryBuilder<ParseObject>(ParseObject('Book'))
65            ..whereGreaterThan('publishingDate', DateTime(2010, 01, 01));
66
67      // Query BookStore using inner Book query
68      final QueryBuilder<ParseObject> bookStoreQuery =
69          QueryBuilder<ParseObject>(ParseObject('BookStore'))
70            ..whereMatchesQuery('books', bookQuery);
71  
72      final ParseResponse bookStoreResponse = await bookStoreQuery.query();
73      if (!bookStoreResponse.success) {
74        setState(() {
75          results.clear();
76        });
77      } else {
78        setState(() {
79          results = bookStoreResponse.results as List<ParseObject>;
80        });
81      }
82    }
83  
84    void doQueryRelatedTo() async {
85      // Get Book object
86      final QueryBuilder<ParseObject> bookQuery =
87          QueryBuilder<ParseObject>(ParseObject('Book'))
88            ..whereEqualTo('title', 'Can You Believe It?');
89  
90      final ParseResponse bookResponse = await bookQuery.query();
91      if (!bookResponse.success) {
92        return;
93      }
94
95      final book = bookResponse.results?.first as ParseObject;
96  
97      // Get Author with relation with Book Can You Believe It?
98      final QueryBuilder<ParseObject> authorsQuery =
99          QueryBuilder<ParseObject>(ParseObject('Author'))
100           ..whereRelatedTo('authors', 'Book', book.objectId!);
101 
102     final ParseResponse authorResponse = await authorsQuery.query();
103 
104     if (!authorResponse.success) {
105       setState(() {
106         results.clear();
107       });
108     } else {
109       setState(() {
110         results = authorResponse.results as List<ParseObject>;
111       });
112     }
113   }
114 
115   void doClearResults() async {
116     setState(() {
117       results.clear();
118     });
119   }
120
121   @override
122   Widget build(BuildContext context) {
123     return Scaffold(
124         body: Padding(
125       padding: const EdgeInsets.all(16.0),
126       child: Column(
127         crossAxisAlignment: CrossAxisAlignment.stretch,
128         children: [
129           Container(
130             height: 200,
131             child: Image.network(
132                 'https://blog.back4app.com/wp-content/uploads/2017/11/logo-b4a-1-768x175-1.png'),
133           ),
134           Center(
135             child: const Text('Flutter on Back4app - GeoPoint',
136                 style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
137           ),
138           SizedBox(
139             height: 8,
140           ),
141           Container(
142             height: 50,
143            child: ElevatedButton(
144                 onPressed: doQueryPointer,
145                 child: Text('Query A'),
146                 style: ElevatedButton.styleFrom(primary: Colors.blue)),
147           ),
148           SizedBox(
149             height: 8,
150           ),
151           Container(
152             height: 50,
153             child: ElevatedButton(
154                 onPressed: doQueryMatches,
155                 child: Text('Query B'),
156                 style: ElevatedButton.styleFrom(primary: Colors.blue)),
157           ),
158           SizedBox(
159             height: 8,
160           ),
161           Container(
162             height: 50,
163             child: ElevatedButton(
164                 onPressed: doQueryRelatedTo,
165                 child: Text('Query C'),
166                 style: ElevatedButton.styleFrom(primary: Colors.blue)),
167           ),
168           SizedBox(
169             height: 8,
170           ),
171           Container(
172             height: 50,
173             child: ElevatedButton(
174                 onPressed: doClearResults,
175                 child: Text('Clear results'),
176                 style: ElevatedButton.styleFrom(primary: Colors.blue)),
177           ),
178           SizedBox(
179             height: 8,
180           ),
181           Text(
182             'Result List: ${results.length}',
183           ),
184           Expanded(
185             child: ListView.builder(
186                 itemCount: results.length,
187                 itemBuilder: (context, index) {
188                   final o = results[index];
189                   return Container(
190                     padding: const EdgeInsets.all(4),
191                     decoration:
192                         BoxDecoration(border: Border.all(color: Colors.black)),
193                     child: Text('${o.toString()}'),
194                   );
195                 }),
196           )
197         ],
198       ),
199     ));
200   }
201 }
```

在您的应用程序仪表板中找到您的应用程序 ID 和客户端密钥凭据，网址为 [Back4App 网站](https://www.back4app.com/).

在main.dart中更新您的代码，使用您项目的ApplicationId和ClientKey在Back4app中.

- **keyApplicationId = 应用程序 ID**
- **keyClientKey = 客户端密钥**

运行项目，应用程序将如图所示加载。

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

## 结论

在本指南的最后，您了解了关系查询在 Parse 上是如何工作的，以及如何在 Flutter 应用程序中在 Back4App 上执行它们。在下一个指南中，您将学习如何在 Parse 中处理用户。
