目錄
- 1.視圖
- 2.視圖中列的可空性
- 3.DAO
- 4.流查詢
- 5.高級用途
- 6.注意事項
1.視圖
也可以將SQL 視圖定義 為 Dart 類。為此,請編寫一個抽象類來擴展View。此示例聲明了一個視圖,用于讀取示例中架構中某個類別中添加的待辦事項數量:
abstract class CategoryTodoCount extends View {// Getters define the tables that this view is reading from.Todos get todos;Categories get categories;// Custom expressions can be given a name by defining them as a getter:.Expression<int> get itemCount => todos.id.count(); Query as() =>// Views can select columns defined as expression getters on the class, or// they can reference columns from other tables.select([categories.description, itemCount]).from(categories).join([innerJoin(todos, todos.category.equalsExp(categories.id))]);
}
在 Dart 視圖中,使用
- 抽象 getter 來聲明您將從中讀取的表(例如TodosTable get todos)。
- Expressiongetter 添加列:(例如itemCount => todos.id.count())。
- as用于定義支持視圖的 select 語句的重寫方法。 中引用的列select可能指兩種類型的列:
- 在視圖本身上定義的列(itemCount如上例所示)。
- 在引用表上定義的列(如categories.description示例中所示)。對于這些引用,
表中列定義中使用的類型轉換器等高級漂移功能也會應用于視圖的列。
當被選中時,兩種類型的列都將添加到視圖的數據類中。
最后,需要通過將視圖包含在參數中來將其添加到數據庫或訪問器中 views:
(tables: [Todos, Categories], views: [CategoryTodoCount])
class MyDatabase extends _$MyDatabase {
2.視圖中列的可空性
對于 Dart 定義的視圖,定義為Expressiongetter 的 表達式始終可空。此行為與TypedResult.read(用于從包含自定義列的復雜 select 語句中讀取結果的方法)匹配。
如果引用的列可為空,或者所選表不是來自內連接(因為null在這種情況下整個表可能都是內連接),則引用另一個表的列的列可為空。
從上面的例子來看,
- 該itemCount列可為空,因為它被定義為復雜 Expression
- description引用 的列不可categories.description為空。這是因為它引用了categories,即視圖
select 語句的主表。
3.DAO
當你有大量查詢時,將它們全部放入一個類中可能會變得繁瑣。你可以通過將一些查詢提取到主數據庫類中可用的類中來避免這種情況。考慮以下代碼:
part '../Dart API/todos_dao.g.dart';// the _TodosDaoMixin will be created by drift. It contains all the necessary
// fields for the tables. The <MyDatabase> type annotation is the database class
// that should use this dao.
(tables: [Todos])
class TodosDao extends DatabaseAccessor<MyDatabase> with _$TodosDaoMixin {// this constructor is required so that the main database can create an instance// of this object.TodosDao(MyDatabase db) : super(db);Stream<List<TodoEntry>> todosInCategory(Category category) {if (category == null) {return (select(todos)..where((t) => isNull(t.category))).watch();} else {return (select(todos)..where((t) => t.category.equals(category.id))).watch();}}
}
如果我們現在將類上的注釋更改MyDatabase為**@DriftDatabase(tables: [Todos, Categories], daos: [TodosDao])** 并重新運行代碼生成,todosDao則可以使用生成的 getter 來訪問該 dao 的實例。
4.流查詢
漂移的核心特性是每個查詢都可以轉換為自動更新流。無論查詢返回單行還是多行,無論查詢是從單個表讀取還是連接多個表,這都能正常工作。
基礎知識?
在drift中,可運行的查詢由接口表示Selectable,該接口具有以下方法:
- Future<List> get():運行一次查詢,返回所有行。
- Future getSingle():運行查詢一次,斷言它產生返回的一行。
- Future<T?> getSingleOrNull():類似getSingle(),但允許返回null空結果集。
并且每個方法都有一個匹配的**watch()**返回流的方法:
- Stream<List> watch():監視查詢,返回所有行。
- Stream watchSingle():監視查詢,斷言每次運行查詢時都會報告一行。
- Stream<T?> watchSingleOrNull():類似watchSingle(),但返回空結果集null。
用于構建查詢的所有漂移 API 都會返回一個Selectable可以監視的內容:
Selectable<TodoItem> allItemsAfter(DateTime min) {return select(todoItems)..where((row) => row.createdAt.isBiggerThanValue(min));
}
無論使用哪種方法,都可以使用 創建流allItemsAfter(value).watch()。由于Stream是 Dart 中的常見構建塊,因此大多數框架都可以使用它們:
- 在 Flutter 中,您可以使用 以聲明方式監聽流StreamBuilder。
- Riverpod 可以使用 來包裝流。示例應用StreamProvider中也使用了此技術。
所有漂移流在監聽之后都會發出最新的結果(因此即使表從未改變,您也會收到快照,而不必合并get()和watch())。
5.高級用途
除了監聽查詢之外,您還可以直接監聽表上的更新事件:
Future<void> listenForUpdates() async {final stream = tableUpdates(TableUpdateQuery.onTable(todoItems,limitUpdateKind: UpdateKind.update,));await for (final event in stream) {print('Update on todos table: $event');}
}
請注意,整個查詢流功能都是以漂移方式實現的,因此流更新是一種啟發式方法,可能會比必要的更頻繁地觸發。您也可以手動將表標記為已更新:
void markUpdated() {notifyUpdates({TableUpdate.onTable(todoItems, kind: UpdateKind.insert)});
}
6.注意事項
雖然流對于自動獲取您正在運行的任何查詢的更新非常有用,但了解其功能和局限性至關重要。流查詢在漂移中以啟發式方法實現:對于每個活動流,漂移會跟蹤其正在監聽的表(該信息可從查詢構建器獲取)。每當通過漂移 API 進行插入、更新或刪除操作時,相關查詢都會重新安排并再次運行。
這意味著:
- 數據庫的其他用途(例如原生 SQLite 客戶端)不會觸發流查詢更新。您可以手動注入更新作為解決方法。
- 流查詢的更新頻率通常會超出其應有的水平,因為我們無法僅篩選特定行的更新。這通常不是問題,但需要注意。流查詢通常應該返回相對較少的行,并且執行時的計算開銷不應過大。