流查询(Stream queries)
摘要
在 drift 中观察 SQL 查询。
drift 的一个核心功能是,每个查询都可以转换成一个自动更新的流。无论查询是返回单行还是多行,或者查询是从单个表读取还是连接多个其他表,这都有效。
基础
在 drift 中,一个可运行的查询由 Selectable<T> 接口表示,该接口具有以下方法:
Future<List<T>> get():运行查询一次,返回所有行。Future<T> getSingle():运行查询一次,断言它产生单行并返回。Future<T?> getSingleOrNull():类似于getSingle(),但允许为空结果集返回null。
这些方法中的每一个都有一个匹配的 watch() 方法,返回一个流:
Stream<List<T>> watch():观察查询,返回所有行。Stream<T> watchSingle():观察查询,断言每次查询运行时都报告单行。Stream<T?> watchSingleOrNull():类似于watchSingle(),但将空结果集作为null返回。
所有用于构建查询的 drift API 都返回一个可以被观察的 Selectable:
dart
Selectable<TodoItem> allItemsAfter(DateTime min) {
return managers.todoItems.filter((c) => c.createdAt.isAfter(min));
}- Drift 需要知道查询中涉及哪些表才能观察它们。在大多数情况下,这是自动推断的,但对于自定义查询,此信息是必需的。
当在 drift 文件中定义 SELECT 语句时,drift 会在数据库类中生成一个返回 Selectable 的方法。例如,
sql
allItemsAfter: SELECT * FROM todo_items WHERE created_at > :min;将使 drift 生成此方法:
dart
Selectable<TodoItem> allItemsAfter({required DateTime min}) {
// ...
}无论使用哪种方法,都可以使用 allItemsAfter(value).watch() 创建一个流。由于 Stream 是 Dart 中的常见构建块,因此它们可以被大多数框架使用:
- 在 Flutter 中,你可以使用
StreamBuilder以声明方式侦听流。 - Riverpod 可以使用
StreamProvider包装流。此技术也用于示例应用程序中。
所有 drift 流在侦听它们后都会发出一个最新的结果(因此即使表从未更改,你也会收到一个快照,并且不必组合 get() 和 watch())。
高级用法
除了侦听查询之外,你还可以直接侦听表上的更新事件:
请注意,整个查询流功能是在 drift 中实现的,因此流更新是一种可能比必要时更频繁触发的启发式方法。也可以手动将表标记为已更新:
注意事项
虽然流对于自动获取你正在运行的任何查询的更新很有用,但了解其功能和局限性很重要。流查询在 drift 中是作为一种启发式方法实现的:对于每个活动的流,drift 会跟踪它正在侦听的表(可从查询构建器获得的信息)。每当通过 drift API 进行插入、更新或删除时,关联的查询都会被重新调度并再次运行。
这意味着:
- 数据库的其他用法,例如本机 SQLite 客户端,不会触发流查询更新。你可以手动注入更新作为一种变通方法。
- 流查询通常比它们必须的更新更频繁,因为我们无法仅针对特定行的更新进行过滤。这通常不是问题,但需要注意。流查询通常应返回相对较少的行,并且执行起来在计算上不要太昂贵。