Floor — 是什么,Flutter 中基于 SQLite 的 ORM

作者: IT Sectr 发布日期: 2026-03-13 阅读时间: 9 分钟

Floor — 适用于 Flutter 的 ORM(对象关系映射),在 SQLite 之上提供类型化层。与原始 SQLite 查询不同,Floor 从带注解的 Dart 模型生成 DAO 类。根据 Pub.dev,2024 的数据,Floor 被用于超过 3500 个 Flutter 项目中,并与 drift 和 hive 一起成为最流行的本地数据存储 ORM 解决方案之一。

要点

  • Floor — 基于 SQLite 的 ORM,通过注解生成 DAO 和 Entity 代码
  • 类型安全 — 查询在编译时检查,消除运行时 SQL 错误
  • DAO 模式 — Data Access Object 将 SQL 查询封装在 Dart 方法中
  • 迁移 — 内置支持 SQLite 模式版本管理
  • 响应式 — 通过 Stream 的 Flow 查询实现 UI 自动更新

什么是 Floor?

Floor — 是一个基于 SQLite 构建的适用于 Flutter 和 Dart 的 ORM 库。它使用注解来描述实体(Entity)、数据访问对象(DAO)和数据库(Database)。代码生成通过 build_runnerfloor_generator 完成 — 编译器创建 DAO 实现和数据库管理类。与原始 sqflite 不同,Floor 完全消除了手动将 ResultSet 转换为 Dart 对象的需要,通过类型反射自动将列映射到 Entity 字段。

Floor 遵循 Repository + DAO 模式,Android 开发人员从 Room 中就很熟悉。每个表由一个带有 @Entity 注解的 Dart 类表示,SQL 查询在带有 @dao 注解的接口中分组,数据库在带有 @Database 的抽象类中组装。这种方法严格分离了数据模型和查询逻辑。

根据 Flutter Pulse(2023),Floor 在 28% 需要本地数据库的 Flutter 项目中被选用。选择的主要原因 — 熟悉 SQL(无需学习新的查询语言)和在编译时检查查询。此外,Floor 生成可读的代码,与具有自定义 DSL 的更抽象的 ORM 不同,它易于调试,从而降低了团队中新开发人员的入门门槛。

Floor 架构

Floor 由三层组成:Entity(表模型)、DAO(查询接口)和 Database(入口点)。生成器创建 _$_Entity 实现用于字段映射,以及 _$_Dao 用于执行 SQL。当 Entity 或 DAO 发生变化时,只需重新启动 build_runner — 代码将自动更新。对于模式版本之间的迁移,Floor 使用顺序版本号,这保证了在用户设备上更新应用程序时的数据完整性。

Floor 在 Flutter 中如何工作?

Floor 通过 sqflite 包使用 SQLite 用于平台构建,以及 sqlite3 用于桌面和 Web。在应用程序启动时,Floor 创建或打开 SQLite 文件,应用迁移并准备 DAO 方法以执行查询。所有操作通过 FutureStream 以异步模式执行。

Floor 中的代码生成按以下原理工作:解析器从源代码读取注解,创建模型和查询的 AST(抽象语法树),然后生成带有 _$ 前缀的 Dart 文件。生成的代码包括 ResultSet → Entity 及反向映射器。

线程安全

Floor 在单个隔离区中使用 SQLite 工作。所有查询异步执行,但并发写入在 SQLite 级别被阻止。对于事务,使用 @transaction 注解,它保证查询组的原子性和错误时的回滚。

Floor vs Drift:Flutter 的 ORM 比较

FloorDrift 都是基于 SQLite 的 ORM,但它们的理念不同。Floor 更接近 Android 中的 Room,Drift 则更具响应性,具有内置的 Stream API 和通过 SQL 文件的查询编译。它们之间的选择取决于团队的经验和所需的响应性。

特性FloorDrift
查询类型@Query 中的 SQL 字符串Dart 方法 + sql 文件
代码生成floor_generator (build_runner)drift_dev (build_runner)
响应性来自 DAO 的 Stream内置 Stream API + 自动更新
复杂性低(熟悉的 SQL)中等(自己的 DSL)
迁移手动 SQL 脚本自动 + 手动
兼容性Android、iOS、macOSAndroid、iOS、Web、macOS、Linux

何时选择 Floor

Floor — 适合已经熟悉 SQL 和 Android Room 的团队。如果开发人员习惯于手动编写 SQL 查询并希望在 SQLite 之上有一个最小包装 — Floor 提供了无需学习新 DSL 的类型化。它也更易于调试,因为生成的代码可读且可预测。

何时 Drift 更好

Drift 提供更强大的响应性并支持更多平台。如果应用程序积极使用 Stream 更新 UI,需要带有 JOIN 和子查询的复杂查询,或者为 Web 构建 — Drift 更可取。然而,由于需要学习自己的 DSL,其入门门槛更高。

Floor 代码示例

Floor 围绕注解构建。下面是一个用于任务列表应用程序的 Entity、DAO 和 Database 的完整示例。运行 build_runner 后,生成的类即可使用。

定义 Entity 和 DAO

TaskEntity 类带有 @Entity 注解,映射到 task 表。带有 @primaryKey 的字段成为主键。TaskDao 接口包含用于表操作的方法 — 每个方法使用 @Query、@Insert、@Update 或 @Delete 注解。

dart
@entity
class TaskEntity {
    @PrimaryKey(autoGenerate: true)
    final int id;
    final String title;
    final bool isCompleted;
    final int priority;

    TaskEntity({this.id, required this.title,
        this.isCompleted = false, this.priority = 0});
}

@dao
abstract class TaskDao {
    @Query('SELECT * FROM TaskEntity ORDER BY priority DESC')
    Future<List<TaskEntity>> getAllTasks();

    @Insert
    Future<int> insertTask(TaskEntity task);

    @Update
    Future<void> updateTask(TaskEntity task);

    @Query('SELECT * FROM TaskEntity WHERE isCompleted = :status')
    Stream<List<TaskEntity>> watchTasks(bool status);
}

初始化数据库

带有 @Database 注解的抽象类连接 Entity 和 DAO。databaseBuilder 方法创建数据库实例。调用 build 后,数据库准备就绪:Floor 打开 SQLite 文件,应用迁移并返回 DAO 以供使用。

dart
@Database(version: 1, entities: [TaskEntity])
abstract class AppDatabase extends FloorDatabase {
    TaskDao get taskDao;
}

// 使用
final database = await $FloorAppDatabase.databaseBuilder('app.db').build();
final taskDao = database.taskDao;
final tasks = await taskDao.getAllTasks();

通过 Stream 的响应式查询

Floor 支持从 DAO 方法返回 Stream。当表中的任何更改发生时,Stream 发出一个新列表。这与 Flutter 中的 StreamBuilder 集成 — 在添加、修改或删除记录时 UI 会自动更新。

dart
@Query('SELECT * FROM TaskEntity ORDER BY priority DESC')
Stream<List<TaskEntity>> watchAllTasks();

// 在 Flutter 组件中
StreamBuilder<List<TaskEntity>>(
    stream: taskDao.watchAllTasks(),
    builder: (context, snapshot) {
        final tasks = snapshot.data ?? [];
        return ListView.builder(
            itemCount: tasks.length,
            itemBuilder: (_, i) => TaskTile(tasks[i]),
        );
    },
)

Floor 中的迁移和版本管理

Floor 通过 @Database 注解中的 version 参数支持数据库版本管理。当 Entity 发生变化(添加或删除字段)时,需要增加版本并添加迁移。迁移是一个 Dart 函数,它接收一个事务并执行 ALTER TABLE SQL 查询。

迁移示例

假设在版本 2 中,我们在 TaskEntity 中添加了 dueDate 字段。迁移通过 ALTER TABLE SQL 查询执行。如果未指定迁移,Floor 会调用 MigrationStrategy,可以在其中设置回退(例如,重建表并丢失数据)。

测试 Floor 查询

Floor 不提供内置的模拟框架,但数据库可以在测试中轻松替换。创建 inMemoryDatabaseBuilder — 它在内存中创建一个 SQLite 数据库,模式与生产环境相同。每次测试后,通过 deleteDatabase 清除数据以隔离测试场景。

Floor 中的事务和批量操作

Floor 通过 DAO 方法上的 @transaction 注解支持事务。在事务内部,多个查询按顺序执行,并在出错时保证回滚。通过带有 List<T> 参数的 @Insert 进行批量插入优化了单次调用中插入多个记录 — 这比在循环中逐个插入快几倍。对于大量操作,使用 100–200 条记录的批量插入:这是在资源受限的移动设备上执行速度和 RAM 消耗之间的最佳平衡。

dart
final migration1to2 = Migration(1, 2, (database) async {
    await database.execute(
        'ALTER TABLE TaskEntity ADD COLUMN dueDate TEXT'
    );
});

final database = await $FloorAppDatabase.databaseBuilder('app.db')
    .addMigrations([migration1to2])
    .build();

常见问题

Floor 与原始 sqflite 有何不同?

sqflite 需要手动编写 SQL 查询并将 ResultSet 映射到对象。Floor 自动生成此代码:您描述 Entity 和 DAO,类型化方法返回准备好的 Dart 对象。Floor 还通过注解在编译时检查 SQL 查询。

Floor 支持表之间的关系吗?

Floor 没有像 Room 那样内置的关系注解(ForeignKey、@Relation)。关系通过 @Query 中的手动 SQL JOIN 查询实现。对于复杂的关系模式,最好考虑 Drift 及其内置的关系支持。

如何调试 Floor SQL 查询?

Floor 允许在创建 DatabaseBuilder 时启用 callback 回调 — 向其传递 sqflite.Database 实例,可以在其上附加记录器。或者,使用 floor_doctor 在开发模式下可视化模式和数据。

Floor 可以用于 Web 构建吗?

Floor 使用在 Web 环境中不工作的 sqflite。对于 Web,需要通过 WASM 使用 sqlite3 进行单独构建。在当前版本中,Floor 正式支持 Android、iOS 和 macOS。对于 Web,使用带有 sqlite3 适配器的 Drift

Floor 中的查询缓存如何工作?

Floor 没有内置缓存 — 每个查询都执行到 SQLite。对于重复查询的缓存,使用带有内存缓存的 Repository 层(例如 dart_cache)。Floor 仅生成用于使用 SQLite 的代码,不会在其上添加层。

总结

  • Floor — 基于 SQLite 的 ORM,通过注解生成 Entity、DAO 和 Database 代码
  • 类型安全 — 通过 @Query 注解在编译时检查 SQL 查询
  • DAO 模式 — SQL 查询封装在 Dart 方法中,分离模型和逻辑
  • 迁移 — 通过 Migration 进行模式版本管理,使用手动 ALTER TABLE
  • 响应性 — 来自 DAO 的 Stream 用于在更改时自动更新 UI
  • 限制 — 没有内置关系,不支持 Web 构建
  • 建议 — 对于团队熟悉 SQL 和 Room 方法的 Flutter 项目,选择 Floor

我们将开发一款交钥匙移动应用程序

IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。

讨论项目

另请阅读