「这是我参与11月更文挑战的第22天,活动详情查看:2021最后一次更文挑战」。
Flutter持久化库drift(原moor)官方文档翻译汇总 - 掘金 (juejin.cn)
本文翻译自 drift 的 官方文档 Dart tables (simonbinder.eu)。
肉翻多有不足,不吝赐教。
Dart 表
Dart 表的高阶内容。
更喜欢用 sql?如果更喜欢用 sql,你也可以通过
CREATE TABLE语句来声明表。drift 的 sql 分析器会生成匹配的 Dart 代码。详细(中文版)
正如在开始指南(中文版)中介绍的,可以用 Dart 来写 sql 表。
class Todos extends Table {
IntColumn get id => integer().autoIncrement()();
TextColumn get title => text().withLength(min: 6, max: 32)();
TextColumn get content => text().named('body')();
IntColumn get category => integer().nullable()();
}
此文会覆盖该语法的高级特性。
命名
drift 在数据库中默认使用 Dart 中 getter 值的蛇形命名(下划线连接小写单词)。
如下表:
class EnabledCategories extends Table {
IntColumn get parentCategory => integer()();
// ..
}
会生成 sql 语句: CREATE TABLE enabled_categories (parent_category INTEGER NOT NULL) 。
如果想要覆写表名,只是需要覆写 tableName 的 getter 。明确的列名可通过 named 方法指定。
class EnabledCategories extends Table {
String get tableName => 'categories';
IntColumn get parentCategory => integer().named('parent')();
}
更新后的类会生成 sql 语句:CREATE TABLE categories (parent INTEGER NOT NULL) 。
想要序列化数据到 json 时改变列名,可为 getter 添加
@JsonKey 。
也可以改变生成的数据表类的类名。
默认,drift 会去掉表名末尾的 s(例如:Users 表会生成 User 的数据类)。
这并不适用于所有情况。比如上面的 EnabeldCategories 表,(默认情况下)会得到 EnabeldCategorie 的数据类。这种情况下,可以使用 @DataClassName 注解来设定期望的表名。
可空(Null)
默认情况下,数据列可能不包含 NULL 值。插入时忘了设值的话,会抛出异常。当使用 sql 时,drift 也会在编译时给出相关警告。
如果想把一个数据列设为可空的,使用 nullable() 即可:
class Items {
IntColumn get category => integer().nullable()();
// ...
}
默认值
可以为数据列设置一个默认值。插入一行新数据时,如果没有明确设值的话,会被设置为默认值。
要设置一个常量默认值,使用 withDefault 方法:
class Preferences extends Table {
TextColumn get name => text()();
BoolColumn get enabled => boolean().withDefault(const Constant(false))();
}
如果之后使用 into(preferences).insert(PreferencesCompanion.forInsert(name: 'foo')),新的数据行的 enabled 列(上面代码中的列)会设为 false (不是 null ,正是我们认为的正常情况)。注意带默认值的列(使用 autoIncrement 或 使用一个默认值),在生成的数据类中还是会被标记为 @required (必需的)。这是因为它们代表着所有数据行,每个数据行的列都有会各自的值。如果要代表部分行,如插入和更新,可以使用数据行的姊妹版(Companion)。
当然,常量只能用于静态值。但是如果想要为(每行的)数据列生成动态默认值,该怎么办?可以使用 clientDefault, 这接收一个函数,返回期望的默认值。每个数据行的插入都会调用这个函数。例如:这里有一个例子使用 uuid 包来生成随机 UUID:
final _uuid = Uuid();
class Users extends Table {
TextColumn get id => text().clientDefault(() => _uuid.v4())();
// ...
}
不知道什么时候该用哪个方法?如果默认值是常量,就使用 withDefault,如 currentDate 这样的简单值。更复杂的情况,如随机生成的值,就需要使用 clientDefault。
在内部,withDefault 会将默认值写入 CREATE TABLE 语句。这会更高效,但是不支持动态值。
主键
如果表中有一个 IntColumn 的列且带有 autoIncrement() 约束, drift 会将其识别为默认的主键。如果想要为表指定自定义的主键,可以在表的类中覆写 primariKey 的 getter。
注意主键在根本上必须是固定不变,这样生成器才能识别。 这意味着:
- 必须使用
syntax定义,不支持函数体。 - 必须返回一个不包含可迭代(如 if/for/扩展操作符)元素的集合。
支持的列类型
drift 本身支持各种列类型。可以使用类型转换器在列中存储自定义类。
| Dart 类型 | 列 | 对应的 SQLite 类型 |
|---|---|---|
int | integer() | INTEGER |
double | real() | REAL |
boolean | boolean() | INTEGER, which a CHECK to only allow 0 or 1 |
String | text() | TEXT |
DateTime | dateTime() | INTEGER (Unix timestamp in seconds) |
Uint8List | blob() | BLOB |
注意 boolean 、 dateTime 的映射和类型转换器仅适用于向数据库中存储数据记录。
它们对 JSON 序列化无任何影响。例如: boolean 值在 fromJson 工厂方法中预期为 true 或 false,即使它们在数据库中会保存为 0 或 1。如果想要一个自定义的 JSON 映射,需要提供专有的 ValueSerializer。
自定义约束
一些列和表的约束在 drift 的 dart API 中不被支持。这包括列上的 REFERENCES 子句,可以通过 customConstraint 来设置:
class GroupMemberships extends Table {
IntColumn get group => integer().customConstraint('NOT NULL REFERENCES groups (id)')();
IntColumn get user => integer().customConstraint('NOT NULL REFERENCES users (id)')();
@override
Set<Column> get primaryKey => {group, user};
}
运用 customConstraint 会覆写其它所有的约束(默认包含的),这意味着需要再把 NOT NULL 约束包含进去。
也可以通过在表中覆写 customConstraints 的 getter 来添加表级别的约束。