Floor — ORM (Object-Relational Mapping) สำหรับ Flutter ที่ให้ชั้นที่ได้รับการกำหนดชนิดเทือบ SQLite ต่างจากคำสั่ง SQLite ดิบ Floor สร้างคลาส DAO จากโมเดล Dart ที่ได้รับการเอนโนเทชัน ตามข้อมูลจาก Pub.dev, 2024, Floor ถูกใช้ในโครงการ Flutter มากกว่า 3500 โครงการ และเป็นหนึ่งในสามอันดับของโซลูชั่น ORM ยอดนิยมที่สุดสำหรับการจัดเก็บข้อมูลในเครืองควบคุมพร้อมกับ drift และ hive
ประเด็นสำคัญ
Floor เป็นไลบรารี ORM สำหรับ Flutter และ Dart ที่สร้างบน SQLite โดยใช้เอนโนเทชันในการอธิบายองค์ประกอบ (Entity), Data Access Objects (DAO) แลฐานข้อมูล (Database) การสร้างโค้ดจะดำเนินการผ่าน build_runner และ floor_generator — ตัวแปลงจะสร้างการนำไปใช้ของ DAO และคลาสจัดการฐานข้อมูล ต่างจาก sqflite ดิบ Floor จะกำจัดความต้องการแปลงด้วยตนจาก ResultSet ไปเป็นอ็อบเจกต์ Dart โดยแมปคอลัมน์ไปยังฟีลด์ Entity โดยอัตโนมัติผ่านการสะท้อนชนิด
Floor ทำตามแบบ Repository + DAO ที่นักพัฒนา Android คุ้นเคยจาก Room แต่ละตารางจะแสดงโดยคลาส Dart ที่มีเอนโนเทชัน @Entity, คำสั่ง SQL จะถูกจัดกลุ่มในอินเทอร์เฟสที่มีเอนโนเทชัน @dao, และฐานข้อมูลจะถูกประกอบขึ้นในคลาสนามธรรมที่มี @Database วิธีการนี้แยกโมเดลข้อมูลออกจากตรรกะคำสั่งอย่างเข้มงวด
ตาม Flutter Pulse (2023), Floor ถูกเลือกใน 28% ของโครงการ Flutter ที่ต้องการฐานข้อมูลในเครือง เหตุผลหลักในการเลือกคือความคุ้นเคยกับ SQL (ไม่ต้องเรียนรู้ภาษาคำสั่งใหม่) และการตรวจสอบคำสั่งในขณะแปลงคอม์ไพเลอร์ นอกจากนี้ Floor สร้างโค้ดที่อ่านง่ายและแก้ไขโค้ดได้ง่ายเมื่อเปรียบเทียบกับ ORM ที่มีนามธรรมมากกว่าพร้อม DSL ที่กำหนดเอง ซึ่งช่วยลดอุปสรรคในการเข้าสู่ทีมสำหรับนักพัฒนาใหม่
Floor ประกอบด้วยสามชั้น: Entity (โมเดลตาราง), DAO (อินเทอร์เฟสคำสั่ง) และ Database (จุดเข้า) ตัวสร้างจะสร้างการนำไปใช้ _$_Entity สำหรับการแมปฟีลด์ และ _$_Dao สำหรับการดำเนินการ SQL เมื่อ Entity หรือ DAO เปลี่ยนแปลง, เพียงแค่เริ่ม build_runner ใหม่ — โค้ดจะอัพเดตโดยอัตโนมัติ สำหรับการย้ายข้ามระหว่างเวอร์ชันโครงสร้าง, Floor ใช้หมายเลขเวอร์ชันแบบลำดับ ซึ่งรับประกันความถูกต้องของข้อมูลเมื่ออัพเดตแอพบนอุปกรณ์ของผู้ใช้
Floor ใช้ SQLite ผ่านแพ็คเกจ sqflite สำหรับบิลด์แพลตฟอร์ม และ sqlite3 สำหรับเดสก์ทอปและเว็บ เมื่อแอพเริ่มทำงาน, Floor จะสร้างหรือเปิดไฟล์ SQLite, ใช้การย้ายข้าม และเตรียมเมธอด DAO สำหรับการดำเนินการคำสั่ง การดำเนินการทั้งหมดจะดำเนินแบบไม่ต้องรอคำตอผ่าน Future และ Stream
การสร้างโค้ดใน Floor ทำงานดังนี้: ตัวแยกอ่านเอนโนเทชันจากไฟล์ต้นฉบับ, สร้าง AST (แบบจำลองโครงสร้างไวยากรณ์) ของโมเดลและคำสั่ง, จากนั้นสร้างไฟล์ Dart ที่มีคำนำหน้า _$ โค้ดที่สร้างขึ้นรวมถึงตัวแมป ResultSet → Entity และในทางกลับ
Floor ทำงานกับ SQLite ในหนึ่ง isolate คำสั่งทั้งหมดจะดำเนินการแบบไม่ต้องรอคำตอบ, แต่การเขียนพร้อมกันจะถูกล็อคในระดับ SQLite สำหรับธุรกรรม, จะใช้เอนโนเทชัน @transaction ซึ่งรับประกันความเป็นเอกภาพของกลุ่มคำสั่งและการย้อนกลับเมื่อเกิดข้อผิดพลาด
ทั้ง Floor และ Drift ล้วนเป็น ORM เทือบ SQLite, แต่แตกต่างกันที่ปรัชญา Floor ใกล้กับ Room ของ Android, Drift ตอบสนองได้มากกว่าด้วย API Stream ในตัวและการคอม์ไพล์คำสั่งผ่านไฟล์ SQL การเลือกระหว่างพวกมันขึ้นอยู่กับประสบการณ์ของทีมและความต้องการการตอบสนอง
| ลักษณะ | Floor | Drift |
|---|---|---|
| ชนิดคำสั่ง | สตริง SQL ใน @Query | เมธอด Dart + ไฟล์ sql |
| การสร้างโค้ด | floor_generator (build_runner) | drift_dev (build_runner) |
| การตอบสนอง | Stream จาก DAO | API Stream ในตัว + อัพเดตอัตโนมัติ |
| ความซับซ้อน | ต่ำ (SQL คุ้นเคย) | ปานกลาง (DSL ของตนเอง) |
| การย้ายข้าม | สคริปต์ SQL ด้วยตน | อัตโนมัติ + ด้วยตน |
| ความเข้ากันได้ | Android, iOS, macOS | Android, iOS, Web, macOS, Linux |
Floor เป็นทางเลือกสำหรับทีมที่คุ้นเคยกับ SQL และ Android Room หากนักพัฒนาเคยเขียนคำสั่ง SQL ด้วยตนเองและต้องการเป็นผ้าห่อหุ้มบน SQLite เล็กน้อย — Floor ให้การกำหนดชนิดโดยไม่ต้องเรียนรู้ DSL ใหม่ นอกจากนี้ยังแก้ไขโค้ดได้ง่ายกว่าเพราะโค้ดที่สร้างขึ้นอ่านง่ายและทำนายได้
Drift ให้การตอบสนองที่มีประสิทธิภาพมากขึ้นและรองรับแพลตฟอร์มมากขึ้น หากแอพใช้ Stream อย่างกรมชันเพื่ออัพเดต UI, ต้องการคำสั่งซับซ้อนกับ JOIN และคำสั่งย่อย หรือมุ่งเป้าไปที่เว็บ — Drift เป็นที่เหมาะสม อย่างไรก็ตาม อุปสรรคในการเข้าใช้งานสูงกว่าเนื่องจากต้องเรียนรู้ DSL ของตัวเอง
Floor สร้างมาจากเอนโนเทชัน ด้านล่างเป็นตัวอย่างสมบูรณ์ของ Entity, DAO และ Database สำหรับแอพรายการทำงาน หลังจากรัน build_runner คลาสที่สร้างขึ้นพร้อมใช้งาน
คลาส TaskEntity ที่มีเอนโนเทชัน @Entity จะแมปไปที่ตาราง task ฟีลด์ที่มี @primaryKey จะกลายเป็นคีย์หลัก อินเทอร์เฟส TaskDao ประกอบด้วยเมธอดสำหรับการดำเนินการกับตาราง — แต่ละเมธอดจะถูกเอนโนเทชันด้วย @Query, @Insert, @Update หรือ @Delete
@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);
}
คลาสนามธรรมที่มีเอนโนเทชัน @Database เชื่อมต่อ Entity และ DAO เมธอด databaseBuilder สร้างอินสแตนซ์ฐานข้อมูล หลังจากเรียก build, ฐานข้อมูลก็พร้อม: Floor เปิดไฟล์ SQLite, ใช้การย้ายข้าม และส่งคืน DAO สำหรับการทำงาน
@Database(version: 1, entities: [TaskEntity])
abstract class AppDatabase extends FloorDatabase {
TaskDao get taskDao;
}
// การใช้งาน
final database = await $FloorAppDatabase.databaseBuilder('app.db').build();
final taskDao = database.taskDao;
final tasks = await taskDao.getAllTasks();
Floor รองรับการส่งคืน Stream จากเมธอด DAO เมื่อมีการเปลี่ยนแปลงในตาราง, Stream จะส่งรายการใหม่ ซึ่งผนวการกับ StreamBuilder ใน Flutter — UI จะอัพเดตโดยอัตโนมัติเมื่อมีการเพิ่ม, แก้ไข หรือลบรือง
@Query('SELECT * FROM TaskEntity ORDER BY priority DESC')
Stream<List<TaskEntity>> watchAllTasks();
// ใน 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]),
);
},
)
Floor รองรับการกำกับเวอร์ชันของฐานข้อมูลผ่านพารามิเตอร์ version ในเอนโนเทชัน @Database เมื่อ Entity เปลี่ยนแปลง (เพิ่มหรือลบฟีลด์), คุณต้องเพิ่มเวอร์ชันและเพิ่มการย้ายข้าม การย้ายข้ามคือฟังก์ชัน Dart ที่รับธุรกรรมและดำเนินการคำสั่ง SQL ALTER TABLE
สมมติว่าในเวอร์ชัน 2 เราเพิ่มฟีลด์ dueDate ใน TaskEntity การย้ายข้ามจะดำเนินการผ่านคำสั่ง SQL ALTER TABLE หากไม่ได้ระบุการย้ายข้าม, Floor จะเรียก MigrationStrategy ซึ่งคุณสามารถตั้งค่า fallback (เช่น, สร้างตารางใหม่โดยสูญเสียข้อมูล)
Floor ไม่มีเฟรมเวิร์กจำลองในตัว, แต่ฐานข้อมูลสามารถแทนที่ได้ง่ายในการทดสอบ สร้าง inMemoryDatabaseBuilder — มันสร้างฐานข้อมูล SQLite ในหน่วยความจำที่มีโครงสร้างเหมือนกับการผลิต หลังจากแต่ละการทดสอบ, ให้ล้างข้อมูลผ่าน deleteDatabase เพื่อแยกสถานการณ์การทดสอบ
Floor รองรับธุรกรรมผ่านเอนโนเทชัน @transaction บนเมธอด DAO ภายในธุรกรรม, หลายคำสั่งจะถูกดำเนินการตามลำดับโดยมีการรับประกันการย้อนกลับเมื่อเกิดข้อผิดพลาด การแทรกข้อมูลแบบชุดผ่าน @Insert กัพารามิเตอร์ List
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();
คำถามที่พบบ่อย
sqflite ต้องการเขียนคำสั่ง SQL ด้วยตนและการแมป ResultSet ไปเป็นอ็อบเจกต์ Floor สร้างโค้ดนี้โดยอัตโนมัติ: คุณอธิบาย Entity และ DAO, และเมธอดที่ได้รับการกำหนดชนิดจะส่งคืนอ็อบเจกต์ Dart ที่พร้อมใช้งาน Floor ยังตรวจสอบคำสั่ง SQL ในขณะแปลงคอม์ไพเลอร์ผ่านเอนโนเทชัน
Floor ไม่มีเอนโนเทชันในตัวสำหรับความสัมพันธ์ (ForeignKey, @Relation) เหมือน Room ความสัมพันธ์จะถูกนำไปใช้ผ่านคำสั่ง SQL JOIN ด้วยตนใน @Query สำหรับโครงสร้างความสัมพันธ์ที่ซับซ้อน, พิจารณา Drift ที่มีการรองรับความสัมพันธ์ในตัว
Floor อนุญาตให้เปิดใช้งาน callback เมื่อสร้าง DatabaseBuilder — โดยจะได้รับอินสแตนซ์ของ sqflite.Database ที่คุณสามารถแนบ logger ได้ อีกทางเลือก, ใช้ floor_doctor เพื่อเบิ่งเห็นโครงสร้างและข้อมูลในโหมด dev
Floor ใช้ sqflite ซึ่งไม่ทำงานในสภาพแวดล้อมเว็บ สำหรับเว็บ, จำเป็นต้องมีการสร้างแยกด้วย sqlite3 ผ่าน WASM ในเวอร์ชันปัจจุบัน, Floor รองรับ Android, iOS และ macOS อย่างเป็นทางการ สำหรับเว็บ, ใช้ Drift กับตัวปรับ sqlite3
Floor ไม่มีแคชในตัว — แต่ละคำสั่งจะถูกดำเนินการบน SQLite สำหรับการแคชคำสั่งที่ทำซ้ำ, ใช้ชั้น Repository ที่มีแคชในหน่วยความจำ (เช่น, dart_cache) Floor สร้างเฉพาะโค้ดสำหรับการทำงานกับ SQLite โดยไม่เพิ่มโอเวอร์เฮดใดๆ
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ
อ่านเพิ่มเติม