[官网文档翻译]Flutter持久化库drift - Dart 表

2,062 阅读4分钟

「这是我参与11月更文挑战的第22天,活动详情查看:2021最后一次更文挑战」。

Flutter持久化库drift(原moor)官方文档翻译汇总 - 掘金 (juejin.cn)

本文翻译自 drift 的 官方文档 Dart tables (simonbinder.eu)

肉翻多有不足,不吝赐教。


重要通知: moor 已改名为 drift 。更多信息[中文]。

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 类型
intinteger()INTEGER
doublereal()REAL
booleanboolean()INTEGER, which a CHECK to only allow 0 or 1
Stringtext()TEXT
DateTimedateTime()INTEGER (Unix timestamp in seconds)
Uint8Listblob()BLOB

注意 booleandateTime 的映射和类型转换器仅适用于向数据库中存储数据记录。 它们对 JSON 序列化无任何影响。例如: boolean 值在 fromJson 工厂方法中预期为 truefalse,即使它们在数据库中会保存为 01。如果想要一个自定义的 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 来添加表级别的约束。