目錄
- 自定義 SQL 類型
- 定義類型
- 使用自定義類型
- 在 Dart 中
- 在 SQL 中
- 方言意識
- 支持的 SQLite 擴展
- json1
- fts5
- 地緣壟斷
自定義 SQL 類型
Drift 的核心庫主要以 SQLite3 為目標平臺編寫。這體現在Drift 開箱即用的SQL 類型上——這些類型由 SQLite3 支持,并新增了一些由 Dart 處理的類型。
其他 Drift 支持有限的數據庫通常支持更多類型。例如,Postgres 為持續時間、JSON 值、UUID 等提供專用類型。對于 sqlite3 數據庫,您需要使用類型轉換器 將這些值存儲為 sqlite3 支持的類型。雖然類型轉換器在這里也可以使用,但它們會指示 Drift 在后臺使用常規文本列。例如,當數據庫內置對 UUID 的支持時,這可能會導致語句效率降低,或與其他與同一數據庫通信的應用程序出現問題。因此,Drift 允許使用“自定義類型”——未在核心drift包中定義且并非適用于所有數據庫的類型。
何時使用自定義類型 - 摘要
當將漂移支持擴展到具有尚未被漂移覆蓋的自身類型的新數據庫引擎時,自定義類型是一個很好的工具。
除非您要擴展 Drift 以使用新的數據庫包(這很棒,請聯系我們!),否則您可能不需要自己實現自定義類型。像 Drift 這樣的包drift_postgres已經為您定義了相關的自定義類型。
定義類型
舉個例子,假設我們有一個數據庫,它原生支持Duration 通過interval類型傳遞值。我們使用的數據庫驅動程序也原生支持Duration值,這意味著值可以通過預處理語句傳遞給數據庫,也可以從行中讀取,而無需手動轉換。
Duration在這種情況下,將添加一個自定義類型類來實現對漂移的支持:
import 'package:drift/drift.dart';class DurationType implements CustomSqlType<Duration> {const DurationType();String mapToSqlLiteral(Duration dartValue) {return "interval '${dartValue.inMicroseconds} microseconds'";}Object mapToSqlParameter(Duration dartValue) => dartValue;Duration read(Object fromSql) => fromSql as Duration;String sqlTypeName(GenerationContext context) => 'interval';
}
此類型定義以下內容:
- 當Duration值映射到 SQL 文字時(例如,因為它們在Constants 中使用),我們interval '123754
microseconds’以 SQL 的方式表示它們。 - 當一個Duration值映射到一個參數時,我們直接使用該值(因為我們假設它受底層數據庫驅動程序支持)。
- 類似地,我們希望數據庫驅動程序正確地將持續時間作為的實例返回 Duration,因此反過來read也只是轉換值。
- CREATE TABLE在語句和強制類型轉換中使用的名稱是interval。
使用自定義類型
在 Dart 中
要在 Dart 表上定義自定義類型,請使用customType具有以下類型的列構建器方法:
import 'package:drift/drift.dart';
import 'type.dart';class PeriodicReminders extends Table {IntColumn get id => integer().autoIncrement()();Column<Duration> get frequency => customType(const DurationType()).clientDefault(() => Duration(minutes: 15))();TextColumn get reminder => text()();
}
如示例所示,其他列約束clientDefault仍然可以添加到自定義列中。如果需要,您甚至可以組合自定義列和類型轉換器。
這足以使大多數查詢正常工作,但在某些高級場景中,您可能需要提供更多信息才能使用自定義類型。例如,當手動構造一個Variable或一個Constant帶有自定義類型的 a 時,必須將自定義類型作為第二個參數添加到構造函數中。這是因為與內置類型不同,drift 沒有一個中央寄存器來描述如何處理自定義類型值。
在 SQL 中
在 SQL 中,Drift 的內聯 Dart語法可用于定義自定義類型:
import 'type.dart';CREATE TABLE periodic_reminders (id INTEGER NOT NULL PRIMARY KEY,frequency `const DurationType()` NOT NULL,reminder TEXT NOT NULL
);
請注意,漂移文件中對自定義類型的支持目前有限。例如,CAST表達式中目前不支持自定義類型。如果您對自定義類型的高級分析支持感興趣,請提交問題或參與討論,并描述您的用例,謝謝!
方言意識
當為某些數據庫管理系統僅支持的 SQL 類型定義自定義類型時,您的數據庫將僅適用于這些數據庫系統。例如,任何使用DurationType 上述定義的表都無法與 sqlite3 兼容,因為它使用的interval類型被 sqlite3 解釋為整數——而interval xyz microsecondssqlite3 根本不支持該語法。
從 Drift 2.15 開始,可以根據所使用的方言定義不同行為的自定義類型。這可以用來為其他數據庫系統構建 polyfill。首先,考慮一個將持續時間存儲為整數的自定義類型,類似于類型轉換器可能執行的操作:
class _FallbackDurationType implements CustomSqlType<Duration> {const _FallbackDurationType();String mapToSqlLiteral(Duration dartValue) {return dartValue.inMicroseconds.toString();}Object mapToSqlParameter(Duration dartValue) {return dartValue.inMicroseconds;}Duration read(Object fromSql) {return Duration(microseconds: fromSql as int);}String sqlTypeName(GenerationContext context) {return 'integer';}
}const durationType = DialectAwareSqlType<Duration>.via(fallback: _FallbackDurationType(),overrides: {SqlDialect.postgres: DurationType(),},
);
通過使用DialectAwareSqlType,您可以在 PostgreSQL 數據庫上自動使用該interval類型,同時在 sqlite3 和其他數據庫上回退到整數類型:
Column<Duration> get frequency => customType(durationType).clientDefault(() => Duration(minutes: 15))();
支持的 SQLite 擴展
在分析.drift文件時,生成器會考慮可能存在的 sqlite3 擴展。但是,生成器無法識別數據庫正在使用的 sqlite3 庫,因此它會默認使用未啟用任何擴展的舊版 sqlite3 庫,并做出悲觀的假設。使用類似 的包時,您將獲得啟用了 json1 和 fts5 擴展的最新 sqlite3 版本。您可以使用構建選項sqlite3_flutter_libs將此信息告知生成器。
json1
要在漂移文件和編譯查詢中啟用 json1 擴展,請修改 構建選項以包含 json1在該sqlite_module部分中。
SQLite 擴展程序不需要任何特殊表,并且適用于所有文本列。在漂移文件和編譯查詢中,json啟用該擴展程序后,所有函數均可用。
由于 json 擴展是可選的,因此在 Dart 中啟用它需要特殊的導入。 package:drift/extensions/json1.dart下面是一個在 Dart 中使用 json 函數的示例:
import 'package:drift/drift.dart';
import 'package:drift/extensions/json1.dart';class Contacts extends Table {IntColumn get id => integer().autoIncrement()();TextColumn get data => text()();
}(tables: [Contacts])
class Database extends _$Database {// constructor and schemaVersion omitted for brevityFuture<List<Contacts>> findContactsWithNumber(String number) {return (select(contacts)..where((row) {// assume the phone number is stored in a json key in the `data` columnfinal phoneNumber = row.data.jsonExtract<String, StringType>('phone_number');return phoneNumber.equals(number);})).get();}
}
您可以在sqlite.org上了解有關 json1 擴展的更多信息。
fts5
fts5 擴展提供了 SQLite 表中的全文搜索功能。要在漂移文件和編譯查詢中啟用 fts5 擴展,請修改 構建選項以包含 fts5在該sqlite_module部分中。
就像您在使用 sqlite 時所期望的那樣,您可以使用CREATE VIRTUAL TABLE語句在漂移文件中創建 fts5 表。
CREATE VIRTUAL TABLE email USING fts5(sender, title, body);
fts5 表上的查詢按預期工作:
emailsWithFts5: SELECT * FROM email WHERE email MATCH 'fts5' ORDER BY rank;
fts5 中的bm25、highlight和snippet函數也可用于自定義查詢。
在 Dart 中無法聲明 fts5 表或在 fts5 表上進行查詢。您可以在sqlite.org上了解有關 fts5 擴展的更多信息。
地緣壟斷
Geopoly 模塊是R-Tree擴展的替代接口,它使用GeoJSON表示法 ( RFC-7946 ) 來描述二維多邊形。Geopoly 包含以下函數:檢測一個多邊形是否包含于另一個多邊形內或與另一個多邊形重疊;計算多邊形的封閉面積;對多邊形進行線性變換;將多邊形渲染為SVG 格式;以及其他類似的操作。
要geopoly在漂移文件和編譯查詢中啟用擴展,請修改 構建選項以包含 geopoly在該sqlite_module部分中。
使用此擴展創建虛擬表的示例:
create virtual table geo using geopoly(geoID, a, b);
SQLite 會接受附加列(如上例中的geoID、a、 )中的任何類型,因此會為這些列生成一個類型,這并不總是很方便。為了避免這種情況,您可以像以下示例一樣添加類型: bdriftDriftAny
create virtual table geo using geopoly (geoID INTEGER not null,a INTEGER,b
);
這將為列類型添加提示,然后 Dart 代碼將更方便使用
您可以在sqlite.org上了解有關 geopoly 擴展的更多信息。