Drift (Moor) — ce este, ORM reactiv și lucrul cu baze de date

Autor: IT Sectr Publicat: 2026-03-13 Timp de citire: 9 min

Drift (fosta Moor) — ORM reactiv pentru Flutter și Dart, construit pe SQLite cu propriul DSL pentru interogări. Spre deosebire de ORM-urile tradiționale, Drift compilează interogările Dart în SQL în etapa de build, eliminând erorile de runtime. Potrivit Drift Docs, 2024, Drift generează cu până la 40% mai mult cod decât interogările SQL manuale, dar elimină complet scrierea manuală a SQL, înlocuind-o cu sintaxa Dart type-safe.

Principalele

  • Drift — ORM reactiv cu compilarea interogărilor în SQL în etapa de build
  • Interogări DSL — interfață fluent în Dart fără a scrie șiruri SQL
  • Reactivitate — Stream și interogări auto-updating pentru actualizarea UI în timp real
  • Multiplatformă — Android, iOS, Web, macOS, Linux, Windows
  • Migrări — versionare automată și migrări manuale prin SQL

Ce este Drift?

Drift — ORM pentru Dart și Flutter, cunoscut anterior ca Moor. A fost dezvoltat de Simon Binder în 2019 și de atunci a trecut prin mai multe versiuni majore. Drift compilează interogările Dart în SQL în etapa de build folosind drift_dev și build_runner, oferind siguranță completă a tipurilor și eliminând erorile de sintaxă SQL în runtime.

Spre deosebire de Floor, Drift folosește propriul DSL (Domain-Specific Language) pentru construirea interogărilor — dezvoltatorul scrie în Dart, iar generatorul traduce acest lucru în SQL. Acest lucru permite IDE-ului să verifice sintaxa, să completeze automat câmpurile tabelelor și să refactorizeze modelul de date fără teama de a strica interogarea.

Potrivit Drift (2024), biblioteca este folosită în peste 8000 de proiecte Flutter. Acceptă toate platformele populare: Android prin sqflite, iOS prin sqflite, web prin sqlite3 WASM, desktop prin driverul nativ sqlite3.

Istoria redenumirii: Moor → Drift

Moor a fost redenumit în Drift în versiunea 2.0 (2022). Motivul — conflictul de nume cu alte proiecte și dorința de a se distanța de codul vechi. API-ul a rămas compatibil: pentru migrare este suficient să înlocuiți importul din moor cu drift și să actualizați dependențele.

Capabilități cheie

Drift oferă: interogări Stream încorporate cu actualizare automată la modificarea datelor, suport pentru tranzacții cu revenire, interogări SQL personalizate prin rawQuery, modelul DAO pentru încapsularea logicii, migrări cross-platform și integrare cu Riverpod și BLoC prin pachetele drift_riverpod și drift_bloc.

Cum funcționează Drift?

Drift utilizează generarea de cod în etapa de compilare. Dezvoltatorul descrie tabelele prin adnotări @DataClass sau clase Dart care extind Table. Generatorul creează clase ajutătoare: Companion (pentru câmpurile nullable la inserare/actualizare), DriftDatabase (punctul de intrare) și implementări DAO.

Interogările SQLite nu sunt executate direct de Drift. În schimb, dezvoltatorul scrie în Dart: select(tasks).where(tasks.priority.greaterThan(3)).build(). Generatorul traduce aceasta în SQL, iar în timpul execuției Drift trimite pur și simplu interogarea SQL pregătită către SQLite. Aceasta combină comoditatea sintaxei Dart cu performanța SQL-ului nativ.

Arhitectura interogărilor

Drift suportă două moduri de interogare: DSL (recomandat) și raw SQL. Interogările DSL sunt mai sigure — compilatorul verifică numele câmpurilor, tipurile și compatibilitatea. Raw SQL este necesar pentru interogări complexe neacoperite de DSL: funcții fereastră, CTE recursive, extensii specifice SQLite.

Drift DSL vs SQL: compararea abordărilor

Drift oferă două moduri de a scrie interogări: Dart DSL (nativ) și raw SQL (pentru cazuri complexe). DSL este preferat în 90% din scenarii: este mai sigur, mai lizibil și suportă refactorizarea. Raw SQL este folosit doar atunci când DSL nu acoperă construcția necesară.

AspectDrift DSLRaw SQL în Drift
Siguranța tipurilorCompletă (compilare)Nu (runtime)
AutocompletareDa (IDE)Doar în fișierele sql
RefactorizareAutomatăCăutare manuală în șiruri
JOIN-uri complexeSuportateLibertate totală
Funcții fereastrăLimitatSuport complet
ReactivitateIncorporată (Stream)Prin .watch()

Când să folosiți DSL

Drift DSL — modul principal de lucru. Acoperă SELECT, INSERT, UPDATE, DELETE, WHERE, ORDER BY, LIMIT, JOIN și grupare. Pentru toate interogările CRUD tipice, folosiți DSL: este mai scurt, mai sigur și actualizează automat Stream-ul la modificări.

Când să folosiți raw SQL

Raw SQL în Drift este necesar pentru: funcții SQLite personalizate (FTS5, JSON1), subinterogări complexe cu EXISTS, INSERT OR REPLACE, UPDATE-uri în masă cu CASE, precum și pentru interogări unde performanța este critică și DSL nu generează un plan de execuție optim. Raw SQL poate fi scris în fișiere .sql cu suport de tipizare prin drift_dev.

Exemple de cod cu Drift

Drift folosește clase care extind Table sau adnotarea @DataClass. Mai jos — un exemplu complet al modelului Task cu interogări prin DSL, raw SQL și actualizare reactivă. După rularea build_runner, toate clasele generate sunt gata.

Definirea tabelului și a bazei de date

Clasa Tasks extinde Table și definește coloanele. Fiecare coloană este o expresie de tip Column<T>. Parametrii: withDefault() setează valoarea implicită, autoIncrement() asigură auto-incrementarea. Baza de date este o clasă abstractă care extinde $DriftDatabase.

dart
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);
}

Operații CRUD prin DSL

Drift generează metodele into(tasks).insert(), select(tasks), update(tasks) și delete(tasks) pentru tabele. Toate operațiile returnează Future — lucrul cu SQLite este asincron. Pentru urmărirea modificărilor, folosiți .watch() în loc de .get().

dart
// Înregistrare
await into(tasks).insert(TasksCompanion.insert(
    title: Value('Cumpără produse'),
    priority: Value(3),
));

// Citire cu filtru
final highPriority = await (select(tasks)
    ..where((t) => t.priority.greaterThan(2))
    ..orderBy([(t) => OrderingTerm(expression: t.priority, mode: OrderingMode.desc)]))
    .get();

// Observare reactivă
select(tasks).watch().listen((tasksList) {
    // tasksList — List, se actualizează la fiecare modificare a tabelului
    updateUi(tasksList);
});

Raw SQL cu rezultat tipizat

Pentru interogări complexe, Drift permite scrierea raw SQL, păstrând în același timp tipizarea. Metoda customSelect primește un șir de interogare și returnează un rezultat tipizat prin generatorul de cod. Această abordare combină flexibilitatea SQL cu siguranța tipurilor Drift.

dart
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")}');
}

Migrări și versionare în Drift

Drift suportă atât migrări automate (pentru modificări simple), cât și manuale (pentru transformări complexe). Versiunea bazei de date se setează în constructorul AppDatabase. La nepotrivirea versiunii, Drift aplică toate migrările neînchise secvențial.

Migrări automate

Pentru adăugarea unei coloane cu valoare implicită, Drift poate genera automat o migrare prin MigrationStrategy. Dacă modificarea nu încalcă datele existente (adăugarea unui câmp nullable), se poate folosi beforeOpen cu verificarea versiunii și executarea ALTER TABLE.

Migrări manuale

Pentru modificări complexe (redenumirea tabelului, îmbinarea datelor, schimbarea tipului coloanei), Drift necesită migrare SQL manuală. Migrările se specifică prin parametrul migrations în clasa bazei de date. Fiecare migrare este un obiect cu numerele from/to și interogări SQL.

Drift și testarea

Drift suportă rularea în modul de test prin NativeDatabase.memory(). Baza de date în memorie este creată de la zero înainte de fiecare test și distrusă după. Pentru mock, folosiți pachetul mocktail cu un QueryExecutor simulat. Drift oferă, de asemenea, DatabaseTestHelper pentru teste de integrare cu verificarea migrărilor și interogărilor.

Modelul DAO în Drift

Drift suportă DAO (Data Access Object) prin clase abstracte cu adnotarea @DriftAccessor. DAO încapsulează interogările către una sau mai multe tabele și poate fi testat separat de baza de date. Spre deosebire de interogările directe prin Database, DAO permite reutilizarea logicii interogărilor între diferite părți ale aplicației și simplifică testarea modulară.

dart
@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);
        }));
    }
}

Întrebări frecvente

Cu ce se deosebește Drift de Floor?

Drift folosește propriul DSL în loc de șiruri SQL, oferind siguranță completă a tipurilor și autocompletare în IDE. Floor folosește șiruri SQL în adnotarea @Query. Drift suportă, de asemenea, mai multe platforme (inclusiv web) și are reactivitate încorporată prin Stream, în timp ce în Floor Stream-ul trebuie declarat manual.

Suportă Drift migrări fără pierderea datelor?

Da, Drift suportă migrări cu păstrarea datelor. Pentru adăugarea coloanelor, folosiți addColumn în Migration. Pentru transformări complexe (redenumire, îmbinare), scrieți raw SQL în interiorul migrării. Dacă migrarea nu este specificată, Drift recrează baza de date cu pierderea datelor la nepotrivirea schemei.

Se poate folosi Drift fără build_runner?

Drift necesită generare de cod prin build_runner și drift_dev. Fără generare, nu se pot crea interogări tipizate. Cu toate acestea, pentru proiecte mici, Drift suportă sqlparser — scrierea manuală a fișierelor SQL cu tipizare automată, dar aceasta necesită totuși etapa de generare.

Cum se integrează Drift cu Riverpod?

Pentru integrarea cu Riverpod, folosiți pachetul drift_riverpod. Acesta oferă provideri pentru Database, DAO și interogări Stream. Exemplu: final tasksProvider = databaseProvider.select((db) => db.select(db.tasks).watch()) — UI-ul se reconstruiește automat la modificarea datelor.

Suportă Drift criptarea SQLite?

Drift nu are criptare încorporată, dar suportă conectarea bibliotecilor sqlite3 personalizate cu SEE (SQLite Encryption Extension). Pentru platforme mobile, folosiți sqflite_sqlcipher ca QueryExecutor — Drift funcționează cu orice implementare SQLite prin intermediul QueryExecutor-ului abstract.

Rezumat

  • Drift — ORM reactiv pentru Flutter și Dart cu compilarea interogărilor în SQL în etapa de build
  • Sintaxa DSL — interogări Dart cu siguranță completă a tipurilor și autocompletare în IDE
  • Reactivitate — Stream și interogări auto-updating pentru actualizarea automată a UI-ului
  • Multiplatformă — Android, iOS, Web (WASM), macOS, Linux, Windows
  • Migrări — automate pentru modificări simple și manuale SQL pentru cele complexe
  • Ecosistem — integrare cu Riverpod (drift_riverpod) și BLoC (drift_bloc)
  • Recomandare — alegeți Drift pentru proiecte unde sunt importante reactivitatea, siguranța tipurilor și suportul pentru toate platformele Flutter

Vom dezvolta o aplicație mobilă la cheie

IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.

Discutați proiectul

Citiți și