Drift (korábban Moor) — reaktív ORM Flutter és Dart számára, SQLite-ra építve saját DSL-lel a lekérdezésekhez. A hagyományos ORM-ekkel ellentétben a Drift a Dart lekérdezéseket SQL-be fordítja a build folyamat során, kiküszöbölve a futásidejű hibákat. A Drift Docs, 2024 szerint a Drift akár 40%-kal több kódot generál, mint a kézi SQL lekérdezések, de teljesen kiküszöböli a kézi SQL írást, típusbiztos Dart szintaxissal helyettesítve azt.
Főbb pontok
Drift — ORM Dart és Flutter számára, korábban Moor néven ismert. Simon Binder fejlesztette 2019-ben és azóta több major verzión ment keresztül. A Drift a Dart lekérdezéseket SQL-be fordítja build időben a drift_dev és build_runner segítségével, ami teljes típusbiztonságot nyújt és kiküszöböli az SQL szintaktikai hibákat futásidőben.
A Floor-ral ellentétben a Drift saját DSL-t (Domain-Specific Language) használ a lekérdezések felépítéséhez — a fejlesztő Dart-ban ír, a generátor pedig lefordítja azt SQL-re. Ez lehetővé teszi az IDE számára a szintaxis ellenőrzését, a táblamezők automatikus kiegészítését és az adatmodell refaktorálását anélkül, hogy félteni kellene a lekérdezések eltörésétől.
A Drift (2024) adatai szerint a könyvtárat több mint 8000 Flutter projektben használják. Támogatja az összes népszerű platformot: Android sqflite-en keresztül, iOS sqflite-en keresztül, web sqlite3 WASM-on keresztül, asztali gépeket sqlite3 natív illesztőprogramon keresztül.
Moor-t Drift-re nevezték át a 2.0 verzióban (2022). Az ok — névütközés más projektekkel és a vágy, hogy eltávolodjanak a régi kódtól. Az API kompatibilis maradt: a migrációhoz elég az importot moor-ról drift-re cserélni és frissíteni a függőségeket.
Drift nyújt: beépített Stream lekérdezéseket automatikus frissítéssel adatváltozáskor, tranzakciók támogatását visszagörgetéssel, egyedi SQL lekérdezéseket rawQuery-n keresztül, DAO mintát a logika elkülönítéséhez, platformfüggetlen migrációkat és integrációt Riverpod-del és BLoC-kal a drift_riverpod és drift_bloc csomagokon keresztül.
Drift kódgenerálást használ fordítási időben. A fejlesztő a táblákat @DataClass annotációkon vagy a Table-t kiterjesztő Dart osztályokon keresztül írja le. A generátor segédosztályokat hoz létre: Companion (nullable mezőkhöz beszúrásnál/frissítésnél), DriftDatabase (belépési pont) és DAO implementációk.
A Drift nem hajtja végre közvetlenül az SQLite lekérdezéseket. Ehelyett a fejlesztő Dart-ban ír: select(tasks).where(tasks.priority.greaterThan(3)).build(). A generátor ezt SQL-re fordítja, futásidőben pedig a Drift egyszerűen elküldi a kész SQL lekérdezést az SQLite-nak. Ez egyesíti a Dart szintaxis kényelmét a natív SQL teljesítményével.
Drift két lekérdezési módot támogat: DSL (ajánlott) és raw SQL. A DSL lekérdezések biztonságosabbak — a fordító ellenőrzi a mezőneveket, típusokat és kompatibilitást. A raw SQL a bonyolult lekérdezésekhez kell, amelyeket a DSL nem fed le: ablakfüggvények, rekurzív CTE, specifikus SQLite kiterjesztések.
Drift két módot kínál a lekérdezések írására: Dart DSL (natív) és raw SQL (bonyolult esetekhez). A DSL az esetek 90%-ában előnyösebb: biztonságosabb, olvashatóbb és támogatja a refaktorálást. A raw SQL csak akkor használatos, ha a DSL nem fedi le a szükséges konstrukciót.
| Szempont | Drift DSL | Raw SQL a Drift-ben |
|---|---|---|
| Típusbiztonság | Teljes (fordítás) | Nincs (futásidő) |
| Automatikus kiegészítés | Igen (IDE) | Csak .sql fájlokban |
| Refaktorálás | Automatikus | Kézi keresés a stringekben |
| Összetett JOIN | Támogatott | Teljes szabadság |
| Ablakfüggvények | Korlátozott | Teljes támogatás |
| Reaktivitás | Beépített (Stream) | .watch()-on keresztül |
Drift DSL — az alapértelmezett munkamód. Lefedi a SELECT, INSERT, UPDATE, DELETE, WHERE, ORDER BY, LIMIT, JOIN és csoportosítást. Minden tipikus CRUD lekérdezéshez használja a DSL-t: rövidebb, biztonságosabb és automatikusan frissíti a Stream-et változások esetén.
Raw SQL a Drift-ben a következőkhöz kell: egyedi SQLite függvények (FTS5, JSON1), összetett alekérdezések EXISTS-szel, INSERT OR REPLACE, tömeges UPDATE CASE-sel, valamint olyan lekérdezésekhez, ahol a teljesítmény kritikus és a DSL nem generál optimális végrehajtási tervet. A raw SQL írható .sql fájlokban típusos támogatással a drift_dev-n keresztül.
Drift osztályokat használ, amelyek a Table-t terjesztik ki, vagy @DataClass annotációt. Alább egy teljes Task modell példa DSL-lel, raw SQL-lel és reaktív frissítéssel. A build_runner futtatása után az összes generált osztály készen áll.
A Tasks osztály kiterjeszti a Table-t és definiálja az oszlopokat. Minden oszlop egy Column<T> típusú kifejezés. Paraméterek: a withDefault() alapértelmezett értéket ad meg, az autoIncrement() autoinkrementációt. Az adatbázis egy abstract osztály, amely a $DriftDatabase-t terjeszti ki.
class Tasks extends Table {
IntColumn get id => integer().autoIncrement();
TextColumn get title => text().withDefault(const Constant(''))();
BoolColumn get isCompleted => boolean().withDefault(const Constant(false))();
IntColumn get priority => integer().withDefault(const Constant(0))();
}
@DriftDatabase(tables: [Tasks])
class AppDatabase extends $AppDatabase {
AppDatabase(QueryExecutor e) : super(e);
}
A Drift generálja a into(tasks).insert(), select(tasks), update(tasks) és delete(tasks) metódusokat a táblákhoz. Minden művelet Future-t ad vissza — az SQLite munka aszinkron. A változások követéséhez használja a .watch()-ot a .get() helyett.
// Beszúrás
await into(tasks).insert(TasksCompanion.insert(
title: Value('Élelmiszerek vásárlása'),
priority: Value(3),
));
// Olvasás szűréssel
final highPriority = await (select(tasks)
..where((t) => t.priority.greaterThan(2))
..orderBy([(t) => OrderingTerm(expression: t.priority, mode: OrderingMode.desc)]))
.get();
// Reaktív megfigyelés
select(tasks).watch().listen((tasksList) {
// tasksList — List, frissül a tábla minden módosításakor
updateUi(tasksList);
});
Összetett lekérdezésekhez a Drift lehetővé teszi a raw SQL írását a típusosság megtartása mellett. A customSelect metódus egy lekérdezés stringet fogad és típusos eredményt ad vissza a kódgenerátoron keresztül. Ez a megközelítés egyesíti az SQL rugalmasságát a Drift típusbiztonságával.
final result = await customSelect(
'SELECT title, COUNT(*) as cnt FROM tasks GROUP BY title',
readsFrom: { tasks },
).get();
for (final row in result) {
print('${row.readString("title")}: ${row.readInt("cnt")}');
}
Drift támogatja mind az automatikus migrációkat (egyszerű változtatásokhoz), mind a kézi migrációkat (bonyolult átalakításokhoz). Az adatbázis verzióját az AppDatabase konstruktorában kell beállítani. Ha a verzió nem egyezik, a Drift egymás után alkalmazza az összes függőben lévő migrációt.
Egy oszlop hozzáadásához alapértelmezett értékkel a Drift automatikusan generálhat migrációt a MigrationStrategy-n keresztül. Ha a változtatás nem sérti a meglévő adatokat (nullable mező hozzáadása), használható a beforeOpen verzióellenőrzéssel és ALTER TABLE végrehajtásával.
Összetett változtatásokhoz (tábla átnevezése, adatok egyesítése, oszloptípus módosítása) a Drift kézi SQL migrációt igényel. A migrációkat a migrations paraméteren keresztül kell megadni az adatbázis osztályban. Minden migráció egy objektum from/to számokkal és SQL lekérdezésekkel.
Drift támogatja a teszt módban való futtatást a NativeDatabase.memory()-n keresztül. Az adatbázis a memóriában a semmiből jön létre minden teszt előtt és megsemmisül utána. A mockoláshoz használja a mocktail csomagot mocked QueryExecutor-ral. A Drift DatabaseTestHelper-t is biztosít integrációs tesztekhez migrációk és lekérdezések ellenőrzésével.
Drift támogatja a DAO (Data Access Object) mintát abstract osztályokon keresztül @DriftAccessor annotációval. A DAO egy vagy több tábla lekérdezéseit kapszulázza be és az adatbázistól függetlenül tesztelhető. A Database-en keresztüli közvetlen lekérdezésekkel ellentétben a DAO lehetővé teszi a lekérdezési logika újrafelhasználását az alkalmazás különböző részei között és egyszerűsíti az egységtesztelést.
@DriftDatabase(tables: [Tasks])
class AppDatabase extends $AppDatabase {
AppDatabase(QueryExecutor e) : super(e) {
migrations.add(Migration(1, 2, (m) async {
await m.addColumn(tasks, tasks.dueDate);
await m.createIndex(tasks.idxPriority);
}));
}
}
Gyakran Ismételt Kérdések
Drift saját DSL-t használ SQL stringek helyett, ami teljes típusbiztonságot és automatikus kiegészítést biztosít az IDE-ben. A Floor SQL stringeket használ a @Query annotációban. A Drift több platformot is támogat (beleértve a webet) és beépített reaktivitással rendelkezik Stream-en keresztül, míg a Floor-ban a Stream-et kézzel kell deklarálni.
Igen, a Drift támogatja az adatmegőrző migrációkat. Oszlopok hozzáadásához használja az addColumn-t a Migration-ben. Összetett átalakításokhoz (átnevezés, egyesítés) írjon raw SQL-t a migráción belül. Ha nincs migráció megadva, a Drift újra létrehozza az adatbázist adatvesztéssel, ha a séma nem egyezik.
Drift kódgenerálást igényel a build_runner és drift_dev segítségével. Generálás nélkül nem lehet típusos lekérdezéseket létrehozni. Kis projektekhez azonban a Drift támogatja a sqlparser-t — SQL fájlok kézi írását automatikus típusosítással, de ehhez is szükség van a generálási lépésre.
A Riverpod-del való integrációhoz használja a drift_riverpod csomagot. Provider-eket biztosít Database-hez, DAO-hoz és Stream lekérdezésekhez. Példa: final tasksProvider = databaseProvider.select((db) => db.select(db.tasks).watch()) — a UI automatikusan újraépül adatváltozáskor.
Drift nem rendelkezik beépített titkosítással, de támogatja az egyedi sqlite3 könyvtárak csatlakoztatását SEE (SQLite Encryption Extension) segítségével. Mobilos platformokhoz használja a sqflite_sqlcipher-t QueryExecutor-ként — a Drift bármely SQLite implementációval működik az absztrakt QueryExecutor-on keresztül.
Összefoglalás
Kulcsrakész mobilalkalmazást fejlesztünk
Az IT Sectr 2017 óta készít iOS és Android alkalmazásokat induló vállalkozásoknak és vállalkozásoknak. Tanácsot adunk, és a legjobb megoldást javasoljuk.
Olvassa el is