「这是我参与11月更文挑战的第26天,活动详情查看:2021最后一次更文挑战」。
Flutter持久化库drift(原moor)官方文档翻译汇总 - 掘金 (juejin.cn)
本文翻译自 drift 的 官方文档 Type converters (simonbinder.eu)。
肉翻多有不足,不吝赐教。
类型转换器
使用类型转换器可以在列中存储复杂的数据。
drift 支持各种各样的类型(开箱即用),但是有时也需要存储复杂些的数据。 可以使用 TypeConverter (类型转换器)来实现这一点。在这个示例中,将会使用 json_serializable 包在一个 text 列中存储一个自定义对象。 drift 对提供的 TypeConverter 支持任意 Dart 类型,在这里我们使用这个包来简化示例。
在 Dart 中使用转换器
import 'dart:convert';
import 'package:json_annotation/json_annotation.dart' as j;
import 'package:drift/drift.dart';
part 'database.g.dart';
@j.JsonSerializable()
class Preferences {
bool receiveEmails;
String selectedTheme;
Preferences(this.receiveEmails, this.selectedTheme);
factory Preferences.fromJson(Map<String, dynamic> json) =>
_$PreferencesFromJson(json);
Map<String, dynamic> toJson() => _$PreferencesToJson(this);
}
接下来,需要告诉 drift 如何在数据库中存储一个 Preferneces 对象。为此编写了 TypeConverter (类型转换器):
// 将 preferences 存储为 字符串
class PreferenceConverter extends TypeConverter<Preferences, String> {
const PreferenceConverter();
@override
Preferences? mapToDart(String? fromDb) {
if (fromDb == null) {
return null;
}
return Preferences.fromJson(json.decode(fromDb) as Map<String, dynamic>);
}
@override
String? mapToSql(Preferences? value) {
if (value == null) {
return null;
}
return json.encode(value.toJson());
}
}
最后,在表声明中使用这个转换器。
class Users extends Table {
IntColumn get id => integer().autoIncrement()();
TextColumn get name => text()();
TextColumn get preferences =>
text().map(const PreferenceConverter()).nullable()();
}
生成的 User 类会有一个 preferences 列,类型为 Preferences。 drift 会自动负责在 select 、 update 、 insert 语句中存储和加载对象。[编译过的自定义查询] (drift.simonbinder.eu/queries/cus…) 同样适用这个特性。
相等比较的警告事项
如果转换器返回了一个不能用值进行比较的对象,生成的数据类也不能用值进行比较。
隐式枚举转换器
一个类型转换器的常见情景是:将枚举表现为下标这样在枚举和整数之间进行映射。因为这个很常见,所以 drift 集成了 intEnum 列类型来简化映射:
enum Status {
none,
running,
stopped,
paused
}
class Tasks extends Table {
IntColumn get id => integer().autoIncrement()();
IntColumn get status => intEnum<Status>()();
}
枚举的警告事项 引入另外的枚举值容易意外地使数据库作废。例如:比方说在上面的示例中向数据库中插入一个
Task,然后把它的status设置为running(下标等于 1 )。现在Status枚举中包含了一个新的成员。
enum Status {
none,
starting, // new!
running,
stopped,
paused
}
当选择 task (任务)时,现在它会报告为 starting ,因为在下标 1 的位置有了新的值。由于这个原因,最好的方式是在枚举的最后添加新的值,这样就不会和现有的值冲突。否则就需要升级 shema 版本,然后运行一个自定义更新语句来修复。
也要注意不能在声明为枚举转换器的列上应用另外一个类型转换器。
在 drift 文件中使用转换器
类型转换器也可以在 drift 文件内部使用。假设 Preferences 和 PreferenceConverter 包含在 preferences.dart中,这个文件可以引入到 drift 文件中,使类型转换器可用。
import 'preferences.dart';
CREATE TABLE users (
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
name TEXT,
preferences TEXT MAPPED BY `const PreferenceConverter()`
);
在 drift 文件中使用类型转换器时,建议设定 apply_converters_on_variables 构建选项。这样在 Dart 转为 sql 时也会应用转换器。例如:如果对 SELECT * FROM users WHERE preferences = ? 的变量使用了这个选项,变量会被当作 Preferences 而不是 String 。
drift 文件对隐式枚举转换器也有专门的支持:
import 'status.dart';
CREATE TABLE tasks (
id INTEGER NOT NULL PRIMARY KEY,
status ENUM(Status)
);
当然,自动枚举转换器的相关警告也适用于 drift 文件。