「这是我参与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 参数会影响到哪些表。这会帮助在之后需要自动更新的其它选择语句。