Floor — nó là gì, ORM trên SQLite trong Flutter

Tác giả: IT Sectr Đã đăng: 2026-03-13 Thời gian đọc: 9 phút

Floor — ORM (Object-Relational Mapping) cho Flutter, cung cấp một lớp được đánh kiểu trên SQLite. Không giống như các truy vấn SQLite thô, Floor tạo ra các lớp DAO từ các mô hình Dart được chú thích. Theo Pub.dev, 2024, Floor được sử dụng trong hơn 3500 dự án Flutter và nằm trong top ba giải pháp ORM phổ biến nhất cho lưu trữ dữ liệu cục bộ cùng với drift và hive.

Điểm Chính

  • Floor — ORM trên SQLite với tạo mã DAO và Entity thông qua annotation
  • An toàn kiểu — các truy vấn được kiểm tra tại thời điểm biên dịch, loại bỏ lỗi SQL runtime
  • Mẫu DAO — Data Access Object đóng gói các truy vấn SQL trong các phương thức Dart
  • Di chuyển — hỗ trợ tích hợp cho việc quản lý phiên bản schema SQLite
  • Tính phản ứng — truy vấn Flow qua Stream để cập nhật UI tự động

Floor là gì?

Floor là một thư viện ORM cho Flutter và Dart được xây dựng trên SQLite. Nó sử dụng annotation để mô tả các thực thể (Entity), Data Access Objects (DAO) và cơ sở dữ liệu (Database). Việc tạo mã được thực hiện thông qua build_runnerfloor_generator — trình biên dịch tạo ra các triển khai DAO và một lớp quản lý cơ sở dữ liệu. Không giống như sqflite thô, Floor loại bỏ hoàn toàn nhu cầu chuyển đổi thủ công từ ResultSet sang đối tượng Dart bằng cách tự động ánh xạ các cột sang trường Entity thông qua phản xạ kiểu.

Floor tuân theo mẫu Repository + DAO quen thuộc với các nhà phát triển Android từ Room. Mỗi bảng được đại diện bởi một lớp Dart với annotation @Entity, các truy vấn SQL được nhóm trong các giao diện với annotation @dao, và cơ sở dữ liệu được tổ hợp trong một lớp trừu tượng với @Database. Cách tiếp cận này phân tách chặt chẽ mô hình dữ liệu khỏi logic truy vấn.

Theo Flutter Pulse (2023), Floor được chọn trong 28% các dự án Flutter yêu cầu cơ sở dữ liệu cục bộ. Lý do chính để chọn nó là quen thuộc với SQL (không cần học ngôn ngữ truy vấn mới) và kiểm tra truy vấn tại thời điểm biên dịch. Ngoài ra, Floor tạo ra mã có thể đọc được, dễ dàng gỡ lỗi so với các ORM trừu tượng hơn với DSL tùy chỉnh, giúp giảm rào cản gia nhập cho các nhà phát triển mới trong nhóm.

Kiến trúc Floor

Floor bao gồm ba lớp: Entity (mô hình bảng), DAO (giao diện truy vấn) và Database (điểm vào). Trình tạo sẽ tạo ra các triển khai _$_Entity cho ánh xạ trường và _$_Dao để thực thi SQL. Khi Entity hoặc DAO thay đổi, chỉ cần khởi động lại build_runner — mã sẽ tự động cập nhật. Để di chuyển giữa các phiên bản schema, Floor sử dụng số phiên bản tuần tự, đảm bảo tính toàn vẹn dữ liệu khi cập nhật ứng dụng trên thiết bị người dùng.

Floor hoạt động như thế nào trong Flutter?

Floor sử dụng SQLite thông qua gói sqflite cho các bản dựng nền tảng và sqlite3 cho máy tính để bàn và web. Khi ứng dụng khởi động, Floor tạo hoặc mở tệp SQLite, áp dụng các di chuyển và chuẩn bị các phương thức DAO để thực thi truy vấn. Tất cả các thao tác được thực hiện bất đồng bộ thông qua FutureStream.

Việc tạo mã trong Floor hoạt động như sau: trình phân tích đọc các annotation từ tệp nguồn, tạo AST (Cây cú pháp trừu tượng) của các mô hình và truy vấn, sau đó tạo các tệp Dart với tiền tố _$. Mã được tạo bao gồm các trình ánh xạ ResultSet → Entity và ngược lại.

An toàn luồng

Floor làm việc với SQLite trong một isolate duy nhất. Tất cả các truy vấn được thực thi bất đồng bộ, nhưng các ghi đồng thời bị khóa ở cấp độ SQLite. Đối với các giao dịch, annotation @transaction được sử dụng, đảm bảo tính nguyên tử của một nhóm truy vấn và rollback khi có lỗi.

Floor vs Drift: so sánh ORM cho Flutter

Cả FloorDrift đều là ORM trên SQLite, nhưng chúng khác nhau về triết lý. Floor gần với Room của Android hơn, Drift phản ứng hơn với API Stream tích hợp và biên dịch truy vấn thông qua tệp SQL. Lựa chọn giữa chúng phụ thuộc vào kinh nghiệm của nhóm và tính phản ứng cần thiết.

Đặc điểmFloorDrift
Loại truy vấnChuỗi SQL trong @QueryPhương thức Dart + tệp sql
Tạo mãfloor_generator (build_runner)drift_dev (build_runner)
Tính phản ứngStream từ DAOAPI Stream tích hợp + tự động cập nhật
Độ phức tạpThấp (SQL quen thuộc)Trung bình (DSL riêng)
Di chuyểnTập lệnh SQL thủ côngTự động + thủ công
Tương thíchAndroid, iOS, macOSAndroid, iOS, Web, macOS, Linux

Khi nào chọn Floor

Floor là lựa chọn cho các nhóm đã quen thuộc với SQL và Android Room. Nếu các nhà phát triển đã quen viết truy vấn SQL thủ công và muốn một lớp bọc tối thiểu trên SQLite — Floor cung cấp khả năng đánh kiểu mà không cần học DSL mới. Nó cũng dễ gỡ lỗi hơn vì mã được tạo có thể đọc được và có thể dự đoán trước.

Khi nào Drift tốt hơn

Drift cung cấp tính phản ứng mạnh mẽ hơn và hỗ trợ nhiều nền tảng hơn. Nếu ứng dụng sử dụng Stream tích cực để cập nhật UI, yêu cầu các truy vấn phức tạp với JOIN và truy vấn con hoặc nhắm mục tiêu web — Drift là lựa chọn tốt hơn. Tuy nhiên, rào cản gia nhập cao hơn do cần học DSL riêng của nó.

Ví dụ mã với Floor

Floor được xây dựng xung quanh các annotation. Dưới đây là một ví dụ đầy đủ về Entity, DAO và Database cho một ứng dụng danh sách công việc. Sau khi chạy build_runner, các lớp được tạo sẵn sàng để sử dụng.

Định nghĩa Entity và DAO

Lớp TaskEntity với annotation @Entity ánh xạ tới bảng task. Trường có @primaryKey trở thành khóa chính. Giao diện TaskDao chứa các phương thức cho các thao tác với bảng — mỗi phương thức được chú thích với @Query, @Insert, @Update hoặc @Delete.

dart
@entity
class TaskEntity {
    @PrimaryKey(autoGenerate: true)
    final int id;
    final String title;
    final bool isCompleted;
    final int priority;

    TaskEntity({this.id, required this.title,
        this.isCompleted = false, this.priority = 0});
}

@dao
abstract class TaskDao {
    @Query('SELECT * FROM TaskEntity ORDER BY priority DESC')
    Future<List<TaskEntity>> getAllTasks();

    @Insert
    Future<int> insertTask(TaskEntity task);

    @Update
    Future<void> updateTask(TaskEntity task);

    @Query('SELECT * FROM TaskEntity WHERE isCompleted = :status')
    Stream<List<TaskEntity>> watchTasks(bool status);
}

Khởi tạo cơ sở dữ liệu

Một lớp trừu tượng với annotation @Database liên kết Entity và DAO. Phương thức databaseBuilder tạo một thể hiện cơ sở dữ liệu. Sau khi gọi build, cơ sở dữ liệu đã sẵn sàng: Floor mở tệp SQLite, áp dụng các di chuyển và trả về DAO để làm việc.

dart
@Database(version: 1, entities: [TaskEntity])
abstract class AppDatabase extends FloorDatabase {
    TaskDao get taskDao;
}

// Sử dụng
final database = await $FloorAppDatabase.databaseBuilder('app.db').build();
final taskDao = database.taskDao;
final tasks = await taskDao.getAllTasks();

Truy vấn phản ứng qua Stream

Floor hỗ trợ trả về Stream từ các phương thức DAO. Khi có bất kỳ thay đổi nào trong bảng, Stream sẽ phát ra một danh sách mới. Điều này tích hợp với StreamBuilder trong Flutter — UI tự động cập nhật khi các bản ghi được thêm, sửa hoặc xóa.

dart
@Query('SELECT * FROM TaskEntity ORDER BY priority DESC')
Stream<List<TaskEntity>> watchAllTasks();

// Trong Flutter widget
StreamBuilder<List<TaskEntity>>(
    stream: taskDao.watchAllTasks(),
    builder: (context, snapshot) {
        final tasks = snapshot.data ?? [];
        return ListView.builder(
            itemCount: tasks.length,
            itemBuilder: (_, i) => TaskTile(tasks[i]),
        );
    },
)

Di chuyển và quản lý phiên bản trong Floor

Floor hỗ trợ quản lý phiên bản cơ sở dữ liệu thông qua tham số version trong annotation @Database. Khi Entity thay đổi (thêm hoặc xóa trường), bạn cần tăng phiên bản và thêm một di chuyển. Di chuyển là một hàm Dart nhận một giao dịch và thực thi các truy vấn SQL ALTER TABLE.

Ví dụ di chuyển

Giả sử ở phiên bản 2, chúng tôi đã thêm trường dueDate vào TaskEntity. Di chuyển được thực hiện thông qua truy vấn SQL ALTER TABLE. Nếu không chỉ định di chuyển, Floor gọi MigrationStrategy nơi bạn có thể đặt fallback (ví dụ: tạo lại bảng với mất dữ liệu).

Kiểm tra truy vấn Floor

Floor không cung cấp framework mẫu tích hợp, nhưng cơ sở dữ liệu có thể dễ dàng được thay thế trong các bài kiểm tra. Tạo inMemoryDatabaseBuilder — nó tạo một cơ sở dữ liệu SQLite trong bộ nhớ có schema giống hệt sản xuất. Sau mỗi bài kiểm tra, hãy xóa dữ liệu qua deleteDatabase để cô lập các kịch bản kiểm tra.

Giao dịch và thao tác hàng loạt trong Floor

Floor hỗ trợ các giao dịch thông qua annotation @transaction trên các phương thức DAO. Bên trong một giao dịch, nhiều truy vấn được thực thi tuần tự với bảo đảm rollback khi có lỗi. Chèn hàng loạt thông qua @Insert với tham số List tối ưu hóa việc chèn nhiều bản ghi trong một lần gọi — điều này nhanh hơn nhiều lần so với việc chèn từng bản ghi một trong một vòng lặp. Đối với các thao tác hàng loạt, hãy sử dụng chèn hàng loạt 100–200 bản ghi: đây là sự cân bằng tối ưu giữa tốc độ thực thi và mức tiêu thụ RAM trên các thiết bị di động có tài nguyên hạn chế.

dart
final migration1to2 = Migration(1, 2, (database) async {
    await database.execute(
        'ALTER TABLE TaskEntity ADD COLUMN dueDate TEXT'
    );
});

final database = await $FloorAppDatabase.databaseBuilder('app.db')
    .addMigrations([migration1to2])
    .build();

Câu Hỏi Thường Gặp

Floor khác gì so với sqflite thô?

sqflite yêu cầu viết thủ công các truy vấn SQL và ánh xạ ResultSet sang đối tượng. Floor tạo mã này tự động: bạn mô tả Entity và DAO, và các phương thức được đánh kiểu trả về các đối tượng Dart sẵn sàng sử dụng. Floor cũng kiểm tra các truy vấn SQL tại thời điểm biên dịch thông qua annotation.

Floor có hỗ trợ mối quan hệ giữa các bảng không?

Floor không có annotation tích hợp cho các mối quan hệ (ForeignKey, @Relation) như Room. Các mối quan hệ được thực hiện thông qua các truy vấn SQL JOIN thủ công trong @Query. Đối với các lược đồ quan hệ phức tạp, hãy cân nhắc Drift với hỗ trợ mối quan hệ tích hợp của nó.

Làm thế nào để gỡ lỗi các truy vấn SQL Floor?

Floor cho phép bật callback khi tạo DatabaseBuilder — nó nhận một thể hiện của sqflite.Database mà bạn có thể gắn logger. Thay vào đó, hãy sử dụng floor_doctor để trực quan hóa schema và dữ liệu ở chế độ dev.

Có thể sử dụng Floor cho các bản dựng web không?

Floor sử dụng sqflite, không hoạt động trong môi trường web. Đối với web, cần một bản dựng riêng với sqlite3 qua WASM. Trong phiên bản hiện tại, Floor chính thức hỗ trợ Android, iOS và macOS. Đối với web, hãy sử dụng Drift với bộ chuyển đổi sqlite3.

Bộ nhớ đệm truy vấn trong Floor hoạt động như thế nào?

Floor không có bộ nhớ đệm tích hợp — mỗi truy vấn được thực thi trên SQLite. Để lưu vào bộ nhớ đệm các truy vấn lặp lại, hãy sử dụng lớp Repository với bộ nhớ đệm trong bộ nhớ (ví dụ: dart_cache). Floor chỉ tạo mã để làm việc với SQLite mà không thêm chi phí trên nó.

Tổng Kết

  • Floor — ORM trên SQLite với tạo mã Entity, DAO và Database thông qua annotation
  • An toàn kiểu — các truy vấn SQL được kiểm tra tại thời điểm biên dịch thông qua annotation @Query
  • Mẫu DAO — các truy vấn SQL được đóng gói trong các phương thức Dart, tách mô hình khỏi logic
  • Di chuyển — quản lý phiên bản schema thông qua Migration với ALTER TABLE thủ công
  • Tính phản ứng — Stream từ DAO để cập nhật UI tự động khi có thay đổi
  • Hạn chế — không có mối quan hệ tích hợp, không hỗ trợ bản dựng web
  • Khuyến nghị — chọn Floor cho các dự án Flutter nơi nhóm quen thuộc với SQL và cách tiếp cận Room

Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay

IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.

Thảo luận dự án

Đọc thêm