Drift (Moor) — Τι είναι, Reactive ORM και εργασία με βάσεις δεδομένων

Συγγραφέας: IT Sectr Δημοσιεύτηκε: 2026-03-13 Χρόνος ανάγνωσης: 9 λεπ

Drift (παλαιότερα Moor) — reactive ORM για Flutter και Dart, χτισμένο πάνω στην SQLite με δικό του DSL για ερωτήματα. Σε αντίθεση με τα παραδοσιακά ORM, το Drift μεταγλωττίζει τα ερωτήματα Dart σε SQL κατά τη φάση κατασκευής, εξαλείφοντας σφάλματα χρόνου εκτέλεσης. Σύμφωνα με το Drift Docs, 2024, το Drift παράγει έως και 40% περισσότερο κώδικα από τα χειροκίνητα SQL ερωτήματα, αλλά εξαλείφει πλήρως τη χειροκίνητη γραφή SQL, αντικαθιστώντας την με type-safe Dart σύνταξη.

Βασικά Σημεία

  • Drift — reactive ORM με μεταγλώττιση ερωτημάτων σε SQL κατά τη φάση κατασκευής
  • DSL ερωτήματα — fluent interface σε Dart χωρίς γραφή SQL συμβολοσειρών
  • Αντιδραστικότητα — Stream και auto-updating ερωτήματα για ενημερώσεις UI σε πραγματικό χρόνο
  • Cross-platform — Android, iOS, Web, macOS, Linux, Windows
  • Μετεγκαταστάσεις — αυτόματη έκδοση και χειροκίνητες μεταναστεύσεις μέσω SQL

Τι είναι το Drift;

Drift — ORM για Dart και Flutter, παλαιότερα γνωστό ως Moor. Αναπτύχθηκε από τον Simon Binder το 2019 και από τότε πέρασε από αρκετές major εκδόσεις. Το Drift μεταγλωττίζει τα Dart ερωτήματα σε SQL κατά τη φάση κατασκευής με τη βοήθεια των drift_dev και build_runner, παρέχοντας πλήρη type-safety και εξαλείφοντας συντακτικά σφάλματα SQL κατά το χρόνο εκτέλεσης.

Σε αντίθεση με το Floor, το Drift χρησιμοποιεί το δικό του DSL (Domain-Specific Language) για την κατασκευή ερωτημάτων — ο προγραμματιστής γράφει σε Dart και ο γεννήτρια το μεταφράζει σε SQL. Αυτό επιτρέπει στο IDE να ελέγχει τη σύνταξη, να συμπληρώνει αυτόματα πεδία πινάκων και να αναδιαμορφώνει το μοντέλο δεδομένων χωρίς φόβο να σπάσει το ερώτημα.

Σύμφωνα με το Drift (2024), η βιβλιοθήκη χρησιμοποιείται σε περισσότερα από 8000 Flutter έργα. Υποστηρίζει όλες τις δημοφιλείς πλατφόρμες: Android μέσω sqflite, iOS μέσω sqflite, web μέσω sqlite3 WASM, desktop μέσω sqlite3 native driver.

Ιστορία μετονομασίας: Moor → Drift

Moor μετονομάστηκε σε Drift στην έκδοση 2.0 (2022). Ο λόγος — σύγκρουση ονομάτων με άλλα έργα και επιθυμία απομάκρυνσης από τον παλιό κώδικα. Το API παρέμεινε συμβατό: για μετεγκατάσταση αρκεί η αλλαγή του import από moor σε drift και η ενημέρωση των dependencies.

Βασικές δυνατότητες

Drift παρέχει: ενσωματωμένα Stream ερωτήματα με αυτόματη ενημέρωση κατά την αλλαγή δεδομένων, υποστήριξη συναλλαγών με αναίρεση, προσαρμοσμένα SQL ερωτήματα μέσω rawQuery, μοτίβο DAO για ενθυλάκωση λογικής, cross-platform μεταναστεύσεις και ενσωμάτωση με Riverpod και BLoC μέσω των πακέτων drift_riverpod και drift_bloc.

Πώς λειτουργεί το Drift;

Drift χρησιμοποιεί δημιουργία κώδικα κατά τη φάση μεταγλώττισης. Ο προγραμματιστής περιγράφει τους πίνακες μέσω @DataClass annotations ή Dart κλάσεων που επεκτείνουν το Table. Ο γεννήτρια δημιουργεί βοηθητικές κλάσεις: Companion (για nullable πεδία κατά την εισαγωγή/ενημέρωση), DriftDatabase (σημείο εισόδου) και υλοποιήσεις DAO.

Το Drift δεν εκτελεί απευθείας ερωτήματα SQLite. Αντίθετα, ο προγραμματιστής γράφει σε Dart: select(tasks).where(tasks.priority.greaterThan(3)).build(). Ο γεννήτρια το μεταφράζει σε SQL και κατά το χρόνο εκτέλεσης το Drift απλώς στέλνει το έτοιμο SQL ερώτημα στην SQLite. Αυτό συνδυάζει την ευκολία της Dart σύνταξης με την απόδοση της native SQL.

Αρχιτεκτονική ερωτημάτων

Drift υποστηρίζει δύο τρόπους ερωτημάτων: DSL (συνιστάται) και raw SQL. Τα DSL ερωτήματα είναι ασφαλέστερα — ο μεταγλωττιστής ελέγχει ονόματα πεδίων, τύπους και συμβατότητα. Το Raw SQL χρειάζεται για σύνθετα ερωτήματα που δεν καλύπτονται από το DSL: συναρτήσεις παραθύρου, recursive CTE, συγκεκριμένες επεκτάσεις SQLite.

Drift DSL vs SQL: σύγκριση προσεγγίσεων

Drift προσφέρει δύο τρόπους γραφής ερωτημάτων: Dart DSL (native) και raw SQL (για σύνθετες περιπτώσεις). Το DSL είναι προτιμότερο για το 90% των σεναρίων: είναι ασφαλέστερο, πιο ευανάγνωστο και υποστηρίζει αναδιάρθρωση. Το Raw SQL χρησιμοποιείται μόνο όταν το DSL δεν καλύπτει την απαιτούμενη κατασκευή.

ΠτυχήDrift DSLRaw SQL στο Drift
Type-safetyΠλήρης (μεταγλώττιση)Όχι (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, μαζικές ενημερώσεις με CASE, καθώς και για ερωτήματα όπου η απόδοση είναι κρίσιμη και το DSL δεν παράγει βέλτιστο σχέδιο εκτέλεσης. Το Raw SQL μπορεί να γραφτεί σε .sql αρχεία με υποστήριξη τύπων μέσω drift_dev.

Παραδείγματα κώδικα με Drift

Drift χρησιμοποιεί κλάσεις που επεκτείνουν το Table ή την annotation @DataClass. Παρακάτω είναι ένα πλήρες παράδειγμα μοντέλου Task με ερωτήματα μέσω DSL, raw SQL και αντιδραστική ενημέρωση. Μετά την εκτέλεση του build_runner, όλες οι παραγόμενες κλάσεις είναι έτοιμες.

Ορισμός πίνακα και βάσης δεδομένων

Η κλάση Tasks επεκτείνει το Table και ορίζει στήλες. Κάθε στήλη είναι μια έκφραση τύπου Column<T>. Παράμετροι: το withDefault() ορίζει προεπιλεγμένη τιμή, το autoIncrement() — αυτόματη αύξηση. Η βάση δεδομένων είναι μια abstract κλάση που επεκτείνει το $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 με mocked QueryExecutor. Το Drift παρέχει επίσης DatabaseTestHelper για δοκιμές ενσωμάτωσης με έλεγχο μεταναστεύσεων και ερωτημάτων.

DAO μοτίβο στο Drift

Drift υποστηρίζει το μοτίβο DAO (Data Access Object) μέσω abstract κλάσεων με annotation @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 συμβολοσειρές, παρέχοντας πλήρη type-safety και αυτοσυμπλήρωση στο IDE. Το Floor χρησιμοποιεί SQL συμβολοσειρές στην annotation @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. Παρέχει providers για 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 — reactive ORM για Flutter και Dart με μεταγλώττιση ερωτημάτων σε SQL κατά τη φάση κατασκευής
  • DSL σύνταξη — Dart ερωτήματα με πλήρη type-safety και αυτοσυμπλήρωση στο IDE
  • Αντιδραστικότητα — Stream και auto-updating ερωτήματα για αυτόματες ενημερώσεις UI
  • Cross-platform — Android, iOS, Web (WASM), macOS, Linux, Windows
  • Μετεγκαταστάσεις — αυτόματες για απλές αλλαγές και χειροκίνητες SQL για σύνθετες
  • Οικοσύστημα — ενσωμάτωση με Riverpod (drift_riverpod) και BLoC (drift_bloc)
  • Σύσταση — επιλέξτε Drift για έργα όπου η αντιδραστικότητα, η ασφάλεια τύπων και η υποστήριξη όλων των πλατφορμών Flutter είναι σημαντικά

Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση

Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.

Συζήτηση έργου

Διαβάστε επίσης