[官网文档翻译]Flutter持久化库drift - 常见问题

11,421 阅读5分钟

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

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

本文翻译自 drift 的 官方文档 Many to many relationships (simonbinder.eu)

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


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

常见问题

使用数据库

如果已经跟着开始指南创建了一个 MyDatabase 类。建议只有一个数据库的实例(单例),这样可以将这个实例存储为一个全局变量:

简单的 Flutter

MyDatabase database;

void main() {
  database = MyDatabase();
  runApp(MyFlutterApp());
}

为此使用 InheritedWidgets 会更简洁,provider 包在这里会起帮助作用:

Provider

如果正在使用 provider 包,可以将高级别组件包装进 provider 来管理数据库实例:

void main() {
  runApp(
    Provider<MyDatabase>(
      create: (context) => MyDatabase(),
      child: MyFlutterApp(),
      dispose: (context, db) => db.close(),
   ),
  );
}

可以使用 Provider.of(context) 在之后的处理中使用组件来访问数据库。

一个更复杂的架构

如果想严格保持组件层外的业务逻辑,可能会使用一些依赖注入框架如 kiwiget_it 来实例化服务和视图模型( view model )。 因此在最想用的 Flutter 依赖注入框架中创建 MyDatabase 的实例会解决这个问题。

为什么有这么多表错误?

如果在安装应用之后添加了另外一个表,需要对新加的表编写一个迁移。如果是在开发应用的过程中,要卸载-重装应用也是可以的。请注意,在 Android 的一些设备上应用的数据可能会被备份,所以手动删除应用的数据而不是重新安装是有必要的。

如何修复生成文件中的提示?

基于你的提示设定,在 drift 生成的 .g.dart 文件中可能会看到一些警告。因为提示主要用于手写代码,所以建议对生成的文件禁用静态分析。对于这个问题,可以在工程下生成一个顶级的文件名为 analysis_options.yaml,然后添加下面的内容:

analyzer:
  exclude:
    - "**/*.g.dart"

可能需要重启 IDE 以应用改变。

如何检查生成的 sql ?

所有的数据库实现( NativeDatabase 、 FlutterQueryExecutor 、 等) 都有一个 logStatements 参数,可以设置为 true 。当为 true 时,drift 会在运行时打印语句。

如何在 APP 第一次启动时插入数据?

可以用自定义的迁移策略在 APP 第一次启动时构成数据库。要在数据库创建时插入数据(通常是在 APP 第一次运行时发生),可以使用如下方式:

MigrationStrategy(
  onCreate: (m) async {
    await m.createAll(); //  创建所有的表
    await into(myTable).insert(...); // 第一次运行时插入
  }
)

甚至可以在 onCreate 回调中使用事务或批处理。

另外一个方式是在应用的 asset 中包含一个事先构成的数据库,然后使用这个数据库:

QueryExecutor databaseWithDefaultAsset(File file, String asset) {
  // LazyDatabase 让们可以作一些异步初始化的工作。
  return LazyDatabase(() async {
    if (!await file.exists()) {
      // 数据库不存在,使用 asset 中的默认数据库
      final content = await rootBundle.load(asset);

      await file.parent.create(recursive: true);
      await file.writeAsBytes(content.buffer.asUint8List(0));
    }
  });
}

drift 和 X 相比如何?

现在有各种各样的用于 Dart 和 Flutter 的好的持久化库

这里有一个好用的库和 drift 比较的不完整(显然也是片面的)的列表。 如果你有任何这些库(或其它库)的经验,也想分享一下和 drift 的比较,邀请你来贡献此页。

sqflite 、 sqlite3

sqflite 是在 iOS 和安卓上绑定了 sqlite api 的 Flutter 包。现在也在正常维护,具有稳定的 api 。事实上,moor_flutter 的变体(?)是基于 sqflite 的。这是即便这样,sqflite 也有一个在 Dart 中构建一些简单查询的 api,drift 通过下面的内容更进了一步:

  • 为查询生成类型安全的映射代码
  • 为查询提供自动更新的流
  • 管理 CREATE_TABLE 语句和大多数 schema 迁移
  • 暴露查询的更顺畅的 api

大多数 APP 仍然不需要这些特性,sqlite 会是一个非常合适的持久化库。

同样的也适用于 sqlite3 包 - package:drift/native.dart 使用了这个库,但是在上层提供了一些附加服务。

sqlcool

sqlcool 是一个和 sqflite 差不多的轻量库,可以简化查询编写和 schema 管理 ,它也有自动更新的流。如果不想(不需要)用 drift 生成代码来解析查询的结果,它可以作为一个相当好的替代方案。

floor

floor 也有很多如自动更新流和 schema 迁移的方便的特性。和 drift 类似,在 Dart 中定义数据库的结构,然后在 sql 中写查询 - 如果生成了映射代码。 drift 也有类似的特性,但是它还可以在编译时验证查询是否是有效的。 drift 还有一个代替 sql 可以在 dart 中写查询的功能。

两者之间的一个不同点是 floor 让你写你自己的类,然后基于写的类生成映射代码。默认,drift 会为你生成大部分类,这更便于使用,但是在一些情况下会使 api 缺少弹性。 drift 还可以使用 自定义数据行类

firebase

实时数据库和云端数据存储都便于使用持久化库,可以用来在设备间同步,即使设备离线。两者都有自动更新流的特性和一个简单的查询 api 。无论如何,它们都不是关系数据库,所以他们不支持如 聚集函数、结合、复杂过滤这样有用的 sql 特性。

有下面的情形时,firebase 是一个很好的选项:

  • 数据模型使用文档表达而不是关系型
  • 不需要有自己的后端,但是需要同步数据。

能查看一个 drift 数据库吗?

是的! drift 在 sqlite3 数据库文件中存储数据,sqlite3 数据库文件可以从设备上提取和在本地查阅。

要直接在设备上查阅一个数据库,可以使用由 Koen Van Looveren 开发的moor_db_viewer 包。