ORM คืออะไร? วิธีที่แอปคุยกับฐานข้อมูล อธิบายแบบเข้าใจง่าย
ORM (object-relational mapper) คือไลบรารีโค้ดที่อยู่ระหว่างแอปกับฐานข้อมูลเชิงสัมพันธ์ แทนที่นักพัฒนาจะเขียนคิวรีฐานข้อมูลด้วย SQL พวกเขาจะทำงานกับข้อมูลในรูปออบเจกต์ปกติของภาษาที่ใช้ เช่น db.booking.create(...) หรือ Booking.find(...) จากนั้น ORM จะแปลงคำสั่งเหล่านั้นเป็น SQL รันคำสั่ง และแปลงผลลัพธ์กลับมาเป็นออบเจกต์ ตัวอย่าง ORM ที่นิยมได้แก่ Prisma และ Drizzle สำหรับ TypeScript, SQLAlchemy สำหรับ Python และ Entity Framework Core สำหรับ C#
ทำไม ORM จึงสำคัญเมื่อสร้างแอปด้วย AI
เมื่อตัวสร้างแอปด้วย AI เพิ่ม ฐานข้อมูล ให้แอปของคุณ มันมักจะเพิ่ม ORM สำหรับคุยกับฐานข้อมูลนั้นด้วย คุณไม่จำเป็นต้องเขียนโค้ด ORM เอง แต่การรู้ว่ามันมีอยู่ช่วยได้มาก:
- โมเดลข้อมูลของคุณอยู่ในโค้ด ไฟล์สคีมาของ ORM มักเป็นคำอธิบายข้อมูลของแอปที่ชัดที่สุด ว่ามีตารางอะไรบ้าง แต่ละตารางมีฟิลด์อะไร และเชื่อมโยงกันอย่างไร การอ่านไฟล์นี้เป็นวิธีที่รวดเร็วในการตรวจว่า AI เข้าใจแอปของคุณถูกต้องหรือไม่
- การเปลี่ยนแปลงฐานข้อมูลผ่าน migration เมื่อคุณขอว่า "เพิ่มเบอร์โทรให้ลูกค้า" AI จะปรับโมเดลและสร้าง migration การรู้เรื่องนี้ช่วยให้คุณเข้าใจว่าทำไมการเปลี่ยนแปลงบางอย่างต้องถูกนำไปใช้ก่อนจึงจะทำงานบนระบบจริงได้
- ปลอดภัยโดยค่าเริ่มต้น ORM สร้างคิวรีในลักษณะที่ป้องกัน SQL injection ซึ่งเป็นช่องโหว่ด้านความปลอดภัยที่พบบ่อยที่สุดในแอปที่ประกอบคิวรีด้วยมือ
- กับดักเรื่องความเร็ว ORM ทำให้เผลอรันคิวรีเล็กๆ หลายร้อยครั้งทั้งที่คิวรีเดียวก็พอ ซึ่งเป็นสาเหตุที่พบบ่อยของหน้าเว็บที่ช้าในแอปที่สร้างด้วย AI
ตัวอย่างเปรียบเทียบในชีวิตประจำวัน
ลองนึกภาพนักท่องเที่ยวชาวอังกฤษกับเจ้าของร้านชาวญี่ปุ่นที่ไม่มีภาษาร่วมกัน และต้องสื่อสารผ่านล่าม
นักท่องเที่ยว (แอปของคุณ) พูดว่า "ขอชาที่ถูกที่สุดสามแบบ" ล่าม (ORM) แปลประโยคนั้นเป็นภาษาญี่ปุ่นที่แม่นยำ (SQL) ส่งให้เจ้าของร้าน (ฐานข้อมูล) แล้วแปลคำตอบกลับเป็นภาษาอังกฤษ
นักท่องเที่ยวไม่ต้องเรียนภาษาญี่ปุ่น และสำหรับการสนทนาส่วนใหญ่ก็ไม่เป็นไร แต่ในการเจรจาที่ซับซ้อน การพูดตรงๆ จะช่วยได้มาก นี่คือเหตุผลที่ ORM ส่วนใหญ่ให้นักพัฒนาเขียน SQL ดิบได้เมื่อจำเป็น
ORM ทำงานอย่างไร
ฐานข้อมูลอย่าง Postgres เก็บข้อมูลในรูปของ ตาราง ที่มีแถวและคอลัมน์ และสืบค้นด้วย SQL ส่วนภาษาโปรแกรมทำงานกับ ออบเจกต์ เช่น การจองที่มีวันที่ ลูกค้า และสถานะ ORM ทำหน้าที่แมปสองฝั่งนี้เข้าด้วยกัน:
| ฝั่งฐานข้อมูล | ฝั่ง ORM / โค้ด |
|---|---|
ตาราง bookings | โมเดล Booking |
| แถว | ออบเจกต์การจองหนึ่งรายการ |
คอลัมน์ starts_at | ฟิลด์ startsAt |
Foreign key customer_id | ความสัมพันธ์ booking.customer |
SELECT, INSERT, UPDATE, DELETE | เมธอด find, create, update, delete |
การดำเนินการทั้งสี่นี้คือการทำ CRUD ซึ่งแอปเกือบทุกแอปใช้เป็นพื้นฐาน
ตัวอย่างการทำงาน: การจองด้วย Prisma
นี่คือสคีมา Prisma แบบย่อสำหรับแอปจองร้านทำผม ซึ่งอธิบายตารางสองตารางและการเชื่อมโยงระหว่างกัน:
model Customer {
id Int @id @default(autoincrement())
name String
email String @unique
bookings Booking[]
}
model Booking {
id Int @id @default(autoincrement())
startsAt DateTime
service String
customer Customer @relation(fields: [customerId], references: [id])
customerId Int
}
เป็นภาษาธรรมดา: ลูกค้าแต่ละคนมีชื่อ อีเมลที่ไม่ซ้ำกัน และการจองได้หลายรายการ การจองแต่ละรายการมีเวลาเริ่ม บริการ และเป็นของลูกค้าหนึ่งคน
ตอนนี้แอปต้องการ "รายการจองที่กำลังจะมาถึงของ Lena เรียงจากเร็วที่สุด":
const bookings = await db.booking.findMany({
where: {
customer: { email: "lena@example.com" },
startsAt: { gte: new Date() },
},
orderBy: { startsAt: "asc" },
});
ORM จะแปลงสิ่งนั้นเป็น SQL ประมาณนี้ รันคิวรี และส่งรายการออบเจกต์การจองกลับมา:
SELECT b.* FROM "Booking" b
JOIN "Customer" c ON c.id = b."customerId"
WHERE c.email = $1 AND b."startsAt" >= $2
ORDER BY b."startsAt" ASC;
$1 และ $2 คือตัวแทนค่า (placeholder) อีเมลและวันที่ถูกส่งแยกจากข้อความคิวรี ซึ่งเป็นสิ่งที่ป้องกัน SQL injection
Migration
ถ้าคุณขอต่อว่า "ให้ลูกค้าเพิ่มเบอร์โทรศัพท์ได้" AI จะเพิ่ม phone String? ในโมเดล Customer (เครื่องหมาย ? หมายถึงไม่บังคับ) และสร้าง migration ซึ่งเป็นสคริปต์ SQL สั้นๆ เช่น ALTER TABLE "Customer" ADD COLUMN "phone" TEXT; Migration ถูกบันทึกตามลำดับ ทำให้ฐานข้อมูลทุกสำเนาปรับโครงสร้างให้เหมือนกันได้ และประวัติการเปลี่ยนแปลงถูกเก็บไว้
ORM ยอดนิยมแยกตามภาษา
| ภาษา | ORM ที่นิยม |
|---|---|
| TypeScript / JavaScript | Prisma, Drizzle, TypeORM, Sequelize |
| Python | SQLAlchemy, Django ORM, SQLModel |
| C# | Entity Framework Core |
| Java | Hibernate (มักใช้ผ่าน Spring Data JPA) |
| Ruby | Active Record |
ORM แต่ละตัวซ่อน SQL ไม่เท่ากัน Prisma ใช้ภาษาสคีมาและรูปแบบคิวรีของตัวเอง ส่วน Drizzle ตั้งใจให้ใกล้เคียงรูปร่างของ SQL พร้อมให้ทุกอย่างมี type ส่วนเครื่องมือที่เบากว่าซึ่งเรียกว่า query builder เช่น Knex หรือ Kysely สำหรับ TypeScript ช่วยสร้าง SQL ในโค้ดโดยไม่ต้องแมปแถวเป็นออบเจกต์เต็มรูปแบบ
คำศัพท์สำคัญ
| คำศัพท์ | ความหมาย |
|---|---|
| Model | นิยามในโค้ดของตารางและฟิลด์ |
| Schema | คำอธิบายทั้งหมดของตาราง ฟิลด์ และความสัมพันธ์ |
| Relation | ความเชื่อมโยงระหว่างโมเดล เช่น การจองที่เป็นของลูกค้าคนหนึ่ง |
| Migration | สคริปต์ที่มีเวอร์ชันซึ่งเปลี่ยนโครงสร้างฐานข้อมูล |
| Query builder | เครื่องมือที่เบากว่าสำหรับสร้าง SQL ในโค้ดโดยไม่ต้องแมปออบเจกต์เต็มรูปแบบ |
| N+1 problem | รันคิวรีหนึ่งครั้งเพื่อดึงรายการ แล้วรันอีกครั้งสำหรับแต่ละรายการ ซึ่งช้าเมื่อข้อมูลเยอะ |
| Raw SQL | SQL ที่เขียนด้วยมือ ใช้เมื่อ ORM เป็นอุปสรรค |
ความเข้าใจผิดที่พบบ่อย
- "ORM คือฐานข้อมูล" ไม่ใช่ มันเป็นตัวแปลภาษา คุณยังต้องมีฐานข้อมูลอย่าง Postgres อยู่
- "ใช้ ORM แล้วไม่ต้องเข้าใจข้อมูลของคุณ" ตารางที่ออกแบบไม่ดีก็สร้างปัญหาผ่าน ORM เช่นกัน การออกแบบโมเดลข้อมูลที่ดีสำคัญกว่าเครื่องมือ
- "ORM ช้าเสมอ" คิวรีประจำวันทั่วไปทำงานได้ดี ความช้ามักมาจากรูปแบบอย่าง N+1 queries หรือการขาด index ซึ่งแก้ไขได้
- "ใช้ ORM แล้วไม่มีความเสี่ยง SQL injection เลยตลอดไป" เมธอดมาตรฐานของ ORM ปลอดภัย แต่ SQL ดิบที่นำข้อมูลจากผู้ใช้มาต่อโดยตรงยังอันตรายอยู่
- "การแก้ไขไฟล์สคีมาจะเปลี่ยนฐานข้อมูลจริงทันที" จะเปลี่ยนก็ต่อเมื่อสร้าง migration และนำไปใช้กับฐานข้อมูลนั้นแล้วเท่านั้น
สิ่งที่ควรถามตัวสร้างแอปด้วย AI ของคุณ
- "แสดงสคีมาฐานข้อมูลให้ฉันดู และอธิบายแต่ละตารางและความสัมพันธ์เป็นภาษาอังกฤษง่ายๆ"
- "โปรเจกต์นี้ใช้ ORM ตัวไหน และ migration อยู่ที่ไหน"
- "ก่อนเปลี่ยนตารางใดๆ บอกฉันว่าข้อมูลที่มีอยู่จะได้รับผลกระทบหรือไม่ และห้ามลบคอลัมน์ที่มีข้อมูลโดยไม่ถามก่อน"
- "ตรวจหน้าที่ช้าที่สุดหาคิวรีแบบ N+1 และเพิ่ม index ในจุดที่มีการกรองหรือเรียงลำดับรายการ"
- "ตรวจว่าทุกคิวรีที่ใช้ข้อมูลจากผู้ใช้ผ่าน ORM หรือใช้พารามิเตอร์ ไม่มี SQL ที่ประกอบจากสตริง"
ORM ใน Mythex
เมื่อแอปของคุณต้องบันทึกข้อมูล Mythex จะเพิ่มฐานข้อมูล Postgres เฉพาะให้โปรเจกต์ ให้ถามในแชตหรือใช้คำสั่ง /database และฉีดข้อมูลการเชื่อมต่อเข้าไปในเวิร์กสเปซและแอปที่เผยแพร่แล้ว (Database) จากนั้นเอเจนต์จะเขียนโค้ดที่อ่านและบันทึกข้อมูลของคุณ หากคุณมีความชอบ เช่น "ใช้ Prisma", "ใช้ SQLAlchemy" หรือ "SQL ธรรมดา ไม่ใช้ ORM" ให้บอกในแชต ขอให้มันอธิบายสคีมาให้ฟังได้ทุกเมื่อ และคุณอ่านไฟล์ได้ในตัวแก้ไขโค้ด ดูภาพรวมได้ที่ ฐานข้อมูลคืออะไร และ SQL กับ NoSQL
คำถาม
ORM คืออะไรแบบง่ายๆ?
ORM (object-relational mapper) คือไลบรารีโค้ดที่อยู่ระหว่างแอปของคุณกับฐานข้อมูลเชิงสัมพันธ์ ทำให้นักพัฒนาอ่านและบันทึกข้อมูลในรูปแบบออบเจกต์ปกติของภาษาที่ใช้เขียนโปรแกรม และเขียนคำสั่ง SQL ให้โดยอัตโนมัติเบื้องหลัง
ตัวอย่าง ORM มีอะไรบ้าง?
ORM ที่นิยม ได้แก่ Prisma และ Drizzle สำหรับ TypeScript และ JavaScript, SQLAlchemy และ Django ORM สำหรับ Python, Entity Framework Core สำหรับ C#, Hibernate สำหรับ Java และ Active Record ใน Ruby on Rails
ORM ดีกว่าการเขียน SQL เองไหม?
ขึ้นอยู่กับงาน ORM ทำให้การอ่านและเขียนข้อมูลประจำวันเขียนได้เร็ว ปลอดภัยกว่า และแก้ไขได้ง่ายกว่า ส่วนการเขียน SQL เองให้การควบคุมเต็มที่สำหรับรายงานที่ซับซ้อนหรือคิวรีที่ต้องการประสิทธิภาพสูง ORM ส่วนใหญ่ยังให้คุณเขียน SQL ดิบเมื่อจำเป็นได้
migration คืออะไร?
migration คือสคริปต์ขนาดเล็กที่มีเวอร์ชันกำกับ ใช้เปลี่ยนโครงสร้างฐานข้อมูล เช่น เพิ่มตารางหรือคอลัมน์ ORM สามารถสร้าง migration จากโมเดลข้อมูลของคุณได้ ทำให้ฐานข้อมูลทุกสำเนา ทั้งบนแล็ปท็อป ในการทดสอบ และในระบบจริง มีโครงสร้างเหมือนกัน