运行时模式自省(Runtime schema introspection)
摘要
使用生成的表类来反射性地检查数据库的模式。
由于 drift 生成的类型安全的表类,在 Dart 中编写 SQL 查询变得简单而安全。但是,这些查询通常是针对特定表编写的。虽然 drift 支持表的继承,但有时以反射方式访问表会更容易。幸运的是,drift 生成的代码实现了可用于执行此操作的接口。
由于这是大多数 drift 用户不需要的主题,因此本页主要提供激励性示例和指向相关 drift 类文档的链接。例如,你可能有多个独立的表,它们都有一个 id 列。你可能希望按其 id 列过滤行。当针对单个表(如入门页面中看到的 Todos 表)编写此查询时,这非常简单:
但是,假设我们想将此查询推广到每个数据库表,那会是什么样子?以下代码段显示了如何做到这一点(请注意,代码段中的链接直接指向相关文档):
由于这比只适用于单个表的查询复杂得多,让我们详细看一下每个有趣的行:
FindById是 ResultSetImplementation 上的一个扩展。此类是 drift 生成的每个表或视图的超类。它定义了用于检查模式或将表示数据库行的原始Map转换为生成的数据类的有用方法。Selectable<Row>表示一个查询,你可以在其上使用get()、watch()、getSingle()和watchSingle()等方法来运行查询。findById中使用的select()扩展可用于在没有对数据库类的引用的情况下启动 select 语句 - 你只需要表实例。- 我们可以使用
columnsByName按其在 SQL 中的名称查找列。在这里,我们期望存在一个int列。 - GeneratedColumn 类表示数据库中的一列。可以从列实例中读取列约束、类型或默认值等内容。
- 特别是,我们使用它来断言表确实有一个名为
id的IntColumn。
- 特别是,我们使用它来断言表确实有一个名为
要调用此扩展,可以使用 await myDatabase.todos.findById(3).getSingle()。将方法定义为扩展的一个好处是类型推断效果很好 - 在 todos 上调用 findById 会返回一个 Todo 实例,即此表的生成数据类。
更新和插入
同样的方法也适用于构造 update、delete 和 insert 语句(尽管这些需要一个 TableInfo 而不是 ResultSetImplementation,因为视图是只读的)。此外,更新和插入使用一个 Insertable 对象,该对象分别表示已更新或已插入列的部分行。对于已知的表,可以使用生成的类型化的 Companion 对象。但是,由于 RawValuesInsertable 的存在,这也可以通过模式自省来完成,RawValuesInsertable 可以用作由列名到值的映射支持的通用 Insertable。
此示例建立在前一个示例的基础上,根据 id 列的过滤器更新通用表的 title 列:
extension UpdateTitle on DatabaseConnectionUser {
Future<Row?> updateTitle<T extends TableInfo<Table, Row>, Row>(
T table,
int id,
String newTitle,
) async {
final columnsByName = table.columnsByName;
final stmt = update(table)
..where((tbl) {
final idColumn = columnsByName['id'];
if (idColumn == null) {
throw ArgumentError.value(
this,
'this',
'Must be a table with an id column',
);
}
if (idColumn.type != DriftSqlType.int) {
throw ArgumentError('Column `id` is not an integer');
}
return idColumn.equals(id);
});
final rows = await stmt.writeReturning(
RawValuesInsertable({'title': Variable<String>(newTitle)}),
);
return rows.singleOrNull;
}
}在数据库或数据库访问器类中,可以像这样调用该方法:
Future<Todo?> updateTodoTitle(int id, String newTitle) {
return updateTitle(todos, id, newTitle);
}希望本页能为你提供一些开始反射性地检查你的 drift 数据库的指针。链接的 Dart 文档也更详细地解释了这些概念。如果你对此有疑问,或者对本页包含更多示例有建议,请随时开始讨论。