Drift (Moor) — шта је, реактивни ORM и рад са базама података

Аутор: IT Sectr Објављено: 2026-03-13 Време читања: 9 мин

Drift (раније Moor) — реактивни ORM за Flutter и Dart, изграђен на SQLite-у са сопственим DSL-ом за упите. За разлику од традиционалних ORM-ова, Drift компајлира Dart упите у SQL у фази изградње, елиминишући runtime грешке. Према Drift Docs, 2024, Drift генерише до 40% више кода од ручних SQL упита, али потпуно елиминише ручно писање SQL-а, замењујући га type-safe Dart синтаксом.

Главно

  • Drift — реактивни ORM са компилацијом упита у SQL у фази изградње
  • DSL упити — fluent интерфејс у Dart-у без писања SQL низова
  • Реактивност — Stream и auto-updating упити за ажурирање UI-ја у реалном времену
  • Вишеплатформност — Android, iOS, Web, macOS, Linux, Windows
  • Миграције — аутоматско верзионисање и ручне миграције кроз SQL

Шта је Drift?

Drift — ORM за Dart и Flutter, раније познат као Moor. Развио га је Simon Binder 2019. године и од тада је прошао кроз неколико главних верзија. Drift компајлира Dart упите у SQL у фази изградње помоћу drift_dev и build_runner, што пружа потпуну типску безбедност и елиминише синтаксне грешке SQL-а у runtime-у.

За разлику од Floor-а, Drift користи сопствени DSL (Domain-Specific Language) за изградњу упита — програмер пише у Dart-у, а генератор то преводи у SQL. Ово омогућава IDE-у да проверава синтаксу, аутоматски допуњава поља табела и рефакторише модел података без страха од кварења упита.

Према Drift (2024), библиотека се користи у преко 8000 Flutter пројеката. Подржава све популарне платформе: Android преко sqflite, iOS преко sqflite, web преко sqlite3 WASM, десктоп преко изворног sqlite3 драјвера.

Историја преименовања: Moor → Drift

Moor је преименован у Drift у верзији 2.0 (2022). Разлог — конфликт назива са другим пројектима и жеља да се дистанцира од старог кода. API је остао компатибилан: за миграцију је довољно заменити import са moor на drift и ажурирати зависности.

Кључне могућности

Drift пружа: уграђене Stream упите са аутоматским ажурирањем при промени података, подршку за трансакције са враћањем, прилагођене SQL упите кроз rawQuery, DAO образац за инкапсулацију логике, вишеплатформске миграције и интеграцију са Riverpod-ом и BLoC-ом кроз пакете drift_riverpod и drift_bloc.

Како ради Drift?

Drift користи генерисање кода у фази компилације. Програмер описује табеле кроз @DataClass анотације или Dart класе које проширују Table. Генератор креће помоћне класе: Companion (за nullable поља при уметању/ажурирању), DriftDatabase (улазна тачка) и DAO имплементације.

SQLite упите Drift не извршава директно. Уместо тога, програмер пише у Dart-у: select(tasks).where(tasks.priority.greaterThan(3)).build(). Генератор ово преводи у SQL, а током извршавања Drift једноставно шаље готов SQL упит SQLite-у. Ово комбинује удобност Dart синтаксе са перформансама изворног SQL-а.

Архитектура упита

Drift подржава два режима упита: DSL (препоручује се) и raw SQL. DSL упити су сигурнији — компајлер проверава имена поља, типове и компатибилност. Raw SQL је потребан за сложене упите које DSL не покрива: прозорске функције, рекурзивне CTE, специфична SQLite проширења.

Drift DSL vs SQL: поређење приступа

Drift нуди два начина писања упита: Dart DSL (изворни) и raw SQL (за сложене случајеве). DSL је пожељнији у 90% сценарија: сигурнији је, читљивији и подржава рефакторисање. Raw SQL се користи само када DSL не покрива потребну конструкцију.

АспектDrift DSLRaw SQL у Drift-у
Типска безбедностПотпуна (компилација)Нема (runtime)
Аутоматско допуњавањеДа (IDE)Само у sql датотекама
РефакторисањеАутоматскоРучна претрага низова
Сложени JOINПодржаниПотпуна слобода
Прозорске функцијеОграниченоПотпуна подршка
РеактивностУграђена (Stream)Кроз .watch()

Када користити DSL

Drift DSL — главни начин рада. Покрива SELECT, INSERT, UPDATE, DELETE, WHERE, ORDER BY, LIMIT, JOIN и груписање. За све типичне CRUD упите користите DSL: краћи је, сигурнији и аутоматски ажурира Stream при променама.

Када користити raw SQL

Raw SQL у Drift-у је потребан за: прилагођене SQLite функције (FTS5, JSON1), сложене подупите са EXISTS, INSERT OR REPLACE, масовне UPDATE са CASE, као и за упите где су перформансе критичне и DSL не генерише оптималан план извршења. Raw SQL се може писати у .sql датотекама са подршком за типизацију кроз drift_dev.

Примери кода са Drift-ом

Drift користи класе које проширују Table или анотацију @DataClass. Испод — потпун пример Task модела са упитима кроз DSL, raw SQL и реактивним ажурирањем. Након покретања build_runner, све генерисане класе су спремне.

Дефинисање табеле и базе података

Класа Tasks проширује Table и дефинише колоне. Свака колона је израз типа Column<T>. Параметри: withDefault() поставља подразумевану вредност, autoIncrement() обезбеђује аутоматско повећање. База података је апстрактна класа која проширује $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);
}

CRUD операције кроз DSL

Drift генерише методе into(tasks).insert(), select(tasks), update(tasks) и delete(tasks) за табеле. Све операције враћају Future — рад са SQLite-ом је асинхрон. За праћење промена користите .watch() уместо .get().

dart
// Упис
await into(tasks).insert(TasksCompanion.insert(
    title: Value('Купи производе'),
    priority: Value(3),
));

// Читање са филтером
final highPriority = await (select(tasks)
    ..where((t) => t.priority.greaterThan(2))
    ..orderBy([(t) => OrderingTerm(expression: t.priority, mode: OrderingMode.desc)]))
    .get();

// Реактивно посматрање
select(tasks).watch().listen((tasksList) {
    // tasksList — List, обновляется при каждом изменении таблицы
    updateUi(tasksList);
});

Raw SQL са типизираним резултатом

За сложене упите Drift омогућава писање raw SQL-а, истовремено чувајући типизацију. Метода customSelect прима низ упита и враћа типизиран резултат кроз генератор кода. Овај приступ комбинује флексибилност SQL-а са сигурношћу типова 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")}');
}

Миграције и верзионисање у Drift-у

Drift подржава како аутоматске миграције (за једноставне промене), тако и ручне (за сложене трансформације). Верзија базе се поставља у конструктору AppDatabase. При неусаглашености верзије, Drift примењује све незатворене миграције секвенцијално.

Аутоматске миграције

За додавање колоне са подразумеваном вредношћу, Drift може аутоматски генерисати миграцију кроз MigrationStrategy. Ако промена не нарушава постојеће податке (додавање nullable поља), може се користити beforeOpen са провером верзије и извршавањем ALTER TABLE.

Ручне миграције

За сложене промене (преименовање табеле, спајање података, промена типа колоне) Drift захтева ручну SQL миграцију. Миграције се задају кроз параметар migrations у класи базе података. Свака миграција је објекат са бројевима from/to и SQL упитима.

Drift и тестирање

Drift подржава покретање у тестном режиму кроз NativeDatabase.memory(). База у меморији се ствара изнова пре сваког теста и уништава после. За моковање користите пакет mocktail са мокованим QueryExecutor-ом. Drift такође пружа DatabaseTestHelper за интеграционе тестове са провером миграција и упита.

DAO образац у Drift-у

Drift подржава DAO (Data Access Object) кроз апстрактне класе са анотацијом @DriftAccessor. DAO инкапсулира упите ка једној или више табела и може се тестирати одвојено од базе података. За разлику од директних упита кроз Database, DAO омогућава поновно коришћење логике упита између различитих делова апликације и поједностављује модуларно тестирање.

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

Често постављана питања

По чему се Drift разликује од Floor-а?

Drift користи сопствени DSL уместо SQL низова, што пружа потпуну типску безбедност и аутоматско допуњавање у IDE-у. Floor користи SQL низове у анотацији @Query. Drift такође подржава више платформи (укључујући web) и има уграђену реактивност кроз Stream, док у Floor-у Stream мора да се декларише ручно.

Да ли Drift подржава миграције без губитка података?

Да, Drift подржава миграције са очувањем података. За додавање колона користите addColumn у Migration. За сложене трансформације (преименовање, спајање) пишите raw SQL унутар миграције. Ако миграција није наведена, Drift поново креира базу са губитком података при неусаглашености шеме.

Може ли се Drift користити без build_runner-а?

Drift захтева генерисање кода кроз build_runner и drift_dev. Без генерисања није могуће креирати типизиране упите. Међутим, за мале пројекте Drift подржава sqlparser — ручно писање SQL датотека са аутоматском типизацијом, али то и даље захтева фазу генерисања.

Како интегрисати Drift са Riverpod-ом?

За интеграцију са Riverpod користите пакет drift_riverpod. Он пружа провајдере за Database, DAO и Stream упите. Пример: final tasksProvider = databaseProvider.select((db) => db.select(db.tasks).watch()) — UI се аутоматски обнавља при промени података.

Да ли Drift подржава шифровање SQLite-а?

Drift нема уграђено шифровање, али подржава повезивање прилагођених sqlite3 библиотека са SEE (SQLite Encryption Extension). За мобилне платформе користите sqflite_sqlcipher као QueryExecutor — Drift ради са било којом имплементацијом SQLite-а кроз апстрактни QueryExecutor.

Резиме

  • Drift — реактивни ORM за Flutter и Dart са компилацијом упита у SQL у фази изградње
  • DSL синтакса — Dart упити са потпуном типском безбедношћу и аутоматским допуњавањем у IDE-у
  • Реактивност — Stream и auto-updating упити за аутоматско ажурирање UI-ја
  • Вишеплатформност — Android, iOS, Web (WASM), macOS, Linux, Windows
  • Миграције — аутоматске за једноставне промене и ручне SQL за сложене
  • Екосистем — интеграција са Riverpod-ом (drift_riverpod) и BLoC-ом (drift_bloc)
  • Препорука — изаберите Drift за пројекте где су важни реактивност, типска безбедност и подршка за све Flutter платформе

Развићемо мобилну апликацију под кључ

IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.

Разговарајте о пројекту

Прочитајте такође