Flutter——数据库Drift开发详细教程(三)

发布于:2025-05-11 ⋅ 阅读:(28) ⋅ 点赞:(0)

参考

正文核心API

写入(更新、插入、删除)

1.更新和删除

您可以使用生成的类来更新任何行的单个字段:

Future moveImportantTasksIntoCategory(Category target) {
  // for updates, we use the "companion" version of a generated class. This wraps the
  // fields in a "Value" type which can be set to be absent using "Value.absent()". This
  // allows us to separate between "SET category = NULL" (`category: Value(null)`) and not
  // updating the category at all: `category: Value.absent()`.
  return (update(todos)
      ..where((t) => t.title.like('%Important%'))
    ).write(TodosCompanion(
      category: Value(target.id),
    ),
  );
}

Future updateTodo(Todo entry) {
  // using replace will update all fields from the entry that are not marked as a primary key.
  // it will also make sure that only the entry with the same primary key will be updated.
  // Here, this means that the row that has the same id as entry will be updated to reflect
  // the entry's title, content and category. As its where clause is set automatically, it
  // cannot be used together with where.
  return update(todos).replace(entry);
}

Future feelingLazy() {
  // 删除id小于10的所有对象
  return (delete(todos)..where((t) => t.id.isSmallerThanValue(10))).go();
}
  • 注意:如果您没有明确添加where更新或删除子句,则该语句将影响表中的所有行!

2.使用 SQL 表达式更新

在某些情况下,您可能需要根据当前值更新多行。一种方案是先将受影响的行选择到 Dart 对象中,然后根据这些结果创建伴随对象并用于更新。如果更新可以用 SQL 描述,则可以使用更高效的方法Companion.custom:

await db
    .update(db.users)
    .write(UsersCompanion.custom(username: db.users.username.lower()));

这里,表name中users所有现有行的列均更改为小写。由于.lower()列的逐一替换功能已在数据库中实现,因此无需在语句执行过程中将行加载到 Dart 中。

3.插入件

您可以非常轻松地将任何有效对象插入表中。由于某些值可能不存在(例如我们无需明确设置的默认值),我们再次使用配套版本。

// returns the generated id
Future<int> addTodo(TodosCompanion entry) {
  return into(todos).insert(entry);
}

生成的所有行类都会有一个可用于创建对象的构造函数:

addTodo(
  TodosCompanion(
    title: Value('Important task'),
    content: Value('Refactor persistence code'),
  ),
);

如果某个列可空或具有默认值(包括自增列),则可以省略该字段。所有其他字段必须已设置且非空。insert否则,该方法将抛出异常。

可以使用批处理高效地运行多个插入语句。为此,您可以使用insertAll中的方法batch:

Future<void> insertMultipleEntries() async{
  await batch((batch) {
    // functions in a batch don't have to be awaited - just
    // await the whole batch afterwards.
    batch.insertAll(todos, [
      TodosCompanion.insert(
        title: 'First entry',
        content: 'My content',
      ),
      TodosCompanion.insert(
        title: 'Another entry',
        content: 'More content',
        // columns that aren't required for inserts are still wrapped in a Value:
        category: Value(3),
      ),
      // ...
    ]);
  });
}

批处理与事务类似,所有更新操作都以原子方式进行,但批处理支持进一步优化,避免重复准备相同的 SQL 语句。这使得批处理非常适合批量插入或更新操作。

4.更新插入

Upserts 是较新版本的 sqlite3 中的一项功能,如果已经存在冲突的行,则它允许插入像更新一样运行。

当主键是其数据的一部分时,这允许我们创建或覆盖现有行:

class Users extends Table {
  TextColumn get email => text()();
  TextColumn get name => text()();

  
  Set<Column> get primaryKey => {email};
}

Future<int> createOrUpdateUser(User user) {
  return into(users).insertOnConflictUpdate(user);
}

当使用已存在的电子邮件地址进行呼叫时createOrUpdateUser(),该用户的姓名将被更新。否则,新的用户将被插入数据库。

插入操作也可以用于更高级的查询。例如,假设我们正在构建一个字典,并希望跟踪某个单词的出现次数。一个用于此目的的表可能如下所示

class Words extends Table {
  TextColumn get word => text()();
  IntColumn get usages => integer().withDefault(const Constant(1))();

  
  Set<Column> get primaryKey => {word};
}

通过使用自定义的 upserts,我们可以插入一个新单词,或者usages 如果它已经存在则增加它的计数器:

Future<void> trackWord(String word) {
  return into(words).insert(
    WordsCompanion.insert(word: word),
    onConflict: DoUpdate(
        (old) => WordsCompanion.custom(usages: old.usages + Constant(1))),
  );
}

5.返回

您可以使用insertReturning插入一行或伴随行,并立即获取插入的行。返回的行包含所有生成的默认值和递增 ID。

注意:此方法使用了RETURNINGsqlite3 3.35 版新增的语法,该语法在大多数操作系统上默认不可用。使用此方法时,请确保您拥有最新的 sqlite3 版本。以下情况也是如此sqlite3_flutter_libs。

例如,考虑使用入门指南中的表格的以下代码片段:

final row = await into(todos).insertReturning(TodosCompanion.insert(
  title: 'A todo entry',
  content: 'A description',
));

返回row的 具有正确的id设置。如果表有其他默认值,包括像 这样的动态值CURRENT_TIME,那么这些值也会在 所返回的行中设置insertReturning。


网站公告

今日签到

点亮在社区的每一天
去签到