如何在 Drift / Flutter 中将数据库与模型映射?

问题描述 投票:0回答:1

我正在使用本地数据库(带有 Drift 的 SQLite) 来保存状态。 存储库读取数据并将数据写入数据库,并且应通过其API提供相应的模型

因此,数据库返回一个 DataClass 对象,我需要 将此数据映射到我的 ModelClass。在存储库中的每个方法中手动进行映射很烦人且容易出错,并且向模型本身添加某种“fromDataClass”和“toDataClass”感觉是错误的,因为模型应该从具体的数据库实现中抽象出来(因此不需要知道它的存储位置)。

但是 Drift 提供了对 TypeConverters 的支持(https://drift.simonbinder.eu/docs/advanced-features/type_converters/),我不确定这是否是正确的方法? 当数据来自本地数据库时,处理数据映射的最佳方式是什么?

flutter sqlite drift
1个回答
0
投票

您提到的类型转换器用于通过将不支持的数据类型编码为 JSON 或转换器中指定的任何自定义格式来存储它们。但是,这种方法无助于将域类映射到数据类或从数据类映射。

您正在寻找的是自定义行类

通过创建表并用

@UseRowClass(YourCustomRowClass)
注释,数据类不会自动生成;相反,将使用您的自定义类。

例如

@UseRowClass(TaskLocalDto)
class Tasks extends Table {
  IntColumn get id => integer().autoIncrement()();
  TextColumn get name => text()();
}

这有什么帮助? 定义自己的数据类使您可以灵活地定义自定义方法、使用 mixin、扩展其他类等。考虑到这一点,我们可以使用

fromDomain
toDomain
创建自定义类像这样的方法:

class TaskLocalDto {
  TaskLocalDto({
    required this.id,
    required this.name,
  });

  final int id;
  final String name;

  factory TaskLocalDto.fromDomain(Task task) => TaskLocalDto(
        id: task.id,
        name: task.name,
      );

  Task toDomain() => Task(
        id: id,
        name: name,
      );
}

现在这个类将成为您从数据库读取时的返回类型。以下方法将返回

TaskLocalDto
:

select(tasks).get();

然后您可以在 DAO 中包含这样的方法:

@DriftAccessor(tables: [Tasks])
class TasksDao extends DatabaseAccessor<AppDatabase> with _$TasksDaoMixin {
  TasksDao(this.db) : super(db);
  final AppDatabase db;

  Future<List<TaskLocalDto>> getTasks() async {
    return select(tasks).get();
  }
}

这适用于读取数据。但是,如果您想使用自定义数据类写入数据,则需要执行额外的步骤。写入数据库需要使用Companion类。 官方漂移文档解释了为什么需要这个额外的课程:

对于部分数据,更喜欢使用同伴。在上面的例子中,我们 仅设置类别列,因此我们使用了同伴。这是为什么 必要的?如果一个字段设置为空,我们不知道我们是否 需要在数据库中将该列设置回空,或者我们是否应该 保持不变即可。同伴中的领域有一个特殊的 Value.absent() 状态使得这一点变得明确。

要使自定义数据类充当伴生类,您需要实现

Insertable<TaskLocalDto>
并重写
Map<String, Expression> toColumns(bool nullToAbsent)
方法。

我们之前的课程现在看起来像这样:

class TaskLocalDto implements Insertable<TaskLocalDto> {
  TaskLocalDto({
    required this.id,
    required this.name,
  });

  final int id;
  final String name;

  factory TaskLocalDto.fromDomain(Task task) => TaskLocalDto(
        id: task.id,
        name: task.name,
      );

  Task toDomain() => Task(
        id: id,
        name: name,
      );

  @override
  Map<String, Expression> toColumns(bool nullToAbsent) {
    return TasksCompanion(
      id: Value(id),
      name: Value(name),
    ).toColumns(nullToAbsent);
  }
}

现在,此方法将允许使用自定义类进行插入操作,并且您可以将其添加到上一个示例中的 DAO 中:

  Future<void> insertTask(TaskLocalDto task) async {
    await into(tasks).insert(task);
  }

提示: 如果您的数据类具有可为空值,请在重写

Value.absentIfNull()
方法时使用
Value()
而不是
toColumns()

您的自定义数据类现在将与您的数据库一起使用,您可以将

toDomain
fromDomain
与它一起使用:

final taskDto = TaskLocalDto.fromDomain(task);

final task = taskDto.toDomain();
© www.soinside.com 2019 - 2024. All rights reserved.