「这是我参与11月更文挑战的第28天,活动详情查看:2021最后一次更文挑战」。
Flutter持久化库drift(原moor)官方文档翻译汇总 - 掘金 (juejin.cn)
本文翻译自 drift 的 官方文档 Custom queries (simonbinder.eu)。
肉翻多有不足,不吝赐教。
自定义查询
让 drift 从 sql 语句生成 dart 代码。
尽管 drift 包括了一个使用流畅的 api 可以用来为大部分语句建模,但是高级特性如 WITH
子句或子查询还没被支持。可以用自定义语句来实现这些特性。当然不要忽略 drift 提供的其它便利:drift 会帮助解析结果数据行、自定义查询也支持自动更新的流。
带有生成的 api 的语句
可以指示 drift 为选择、更新、删除语句自动生成一个类型安全的 api。当然也可以手写自定义的 sql 。细节看一下下面的内容。
要使用这个特性,所有需要做的就是在 DriftDatabase
注解中定义查询:
@DriftDatabase(
tables: [Todos, Categories],
queries: {
'categoriesWithCount':
'SELECT *, (SELECT COUNT(*) FROM todos WHERE category = c.id) AS "amount" FROM categories c;'
},
)
class MyDatabase extends _$MyDatabase {
// 其余部分保持不变
}
再次运行构建步骤之后, drift 会写入一个 CategoriesWithCountResult
- 它会保有查询的结果。
从 _$MyDatabase
类继承的类也会带有 categoriesWithCount
(只运行一次查询) 方法和 watchCategoriesWithCount
(返回一个自动更新的流)方法。
查询可以使用 ?
或 :name
语法来带参数。当查询包含参数时, drift 会推断出它们适当的类型,然后把它们包含进生成的方法中。例如:'categoryById': 'SELECT * FROM categories WHERE id = :id'
会生成 categoryById(int id)
方法。
对于表名
用这个特性,会帮助了解 Dart 表在 sql 中如何命名。对于没有覆写
tableName
的表,在 sql 中的全称为类名的蛇形命名(下划线连接小写单词)。 所以一个Categories
的 Dart 表会命名为categories
,一个UserAddressInformation
的 Dart 表会命名为user_address_information
。 同样的规则也适用于没有明确指定名称的列。 drift 文件中的表和列总是会使用指定的名称。
也可以使用 UDPATE
和 DELETE
语句。当然,这个特性对于 DAO 也可用,它也通过分析读写的表完美集成了自动更新流。
自定义选择(select)语句
如果不想用生成的 api 来调用语句,也可以通过调用单次查询的 customSelect
或查询流的 customSelectStream
发送自定义查询。 查询流会在底层数据改变时自动生成新的项目集合。
通过[开始指南]的 Todo 示例,可以写一个加载每个 cateory 的所有 todo 的数量的查询。
class CategoryWithCount {
final Category category;
final int count; // 当前 category 的实体数
CategoryWithCount(this.category, this.count);
}
// 然后在数据库类里
Stream<List<CategoryWithCount>> categoriesWithCount() {
// 选项所有的 category ,然后加载每个 category 有多少关联的实体。
return customSelect(
'SELECT *, (SELECT COUNT(*) FROM todos WHERE category = c.id) AS "amount" FROM categories c;',
readsFrom: {todos, categories}, // used for the stream: the stream will update when either table changes
).watch().map((rows) {
// 这里有一个数据行的列表。只需要将数据行的原始数据转换为 CategoryWithCount。
// 因为这之前已经定义了 Category 表,drift 知道如何解析一个 category。
// 所以只剩下手动提取数量。
return rows
.map((row) => CategoryWithCount(Category.fromData(row.data, this), row.readInt('amount')))
.toList();
});
}
对于自定义查询,应该使用 readsFrom
参数指定在读取的表。使用 Stream
时, drift 会在流之后监听到,然后更新要产出项目的流。
可以使用问号占位符和 variables
参数来绑定 sql 变量:
Stream<int> amountOfTodosInCategory(int id) {
return customSelect(
'SELECT COUNT(*) AS c FROM todos WHERE category = ?',
variables: [Variable.withInt(id)],
readsFrom: {todos},
).map((row) => row.readInt('c')).watch();
}
当然,也可以使用带下标的变量(例如: ?12
) - 关于这些的更多信息,参考 sqlite3 文档。
自定义更新语句
对于更新和删除语句,可以使用 customUpdate
。就像 customSelect
,customUpdate
方法带有一个 sql 语句参数和一个可选的变量参数。也可以告诉 drift 在查询中使用的可选的 updates
参数会影响到哪些表。这会帮助在之后需要自动更新的其它选择语句。