ทำความรู้จักกับ ORM ตัวช่วยจัดการฐานข้อมูลให้ง่ายขึ้น
ถ้าเราเปรียบเทียบการเขียนโปรแกรมเหมือนการสร้างบ้าน ฐานข้อมูล (ที่เก็บข้อมูลถาวร) ก็คือคลังวัสดุที่ซับซ้อน ปกติเวลาเราจะหยิบของหรือเก็บของ เราต้องเขียนภาษา SQL (ภาษาสำหรับคุยกับฐานข้อมูล) ซึ่งสำหรับมือใหม่ถือว่ายากและต้องระวังข้อผิดพลาดมากทีเดียว
ORM หรือ Object-Relational Mapping (การแปลงข้อมูลจากฐานข้อมูลให้อยู่ในรูปของวัตถุในโค้ด) จึงเข้ามาทำหน้าที่เป็นตัวกลางให้เราครับ แทนที่จะเขียนคำสั่ง SQL ดิบๆ เราก็แค่เขียนโค้ดภาษา JavaScript หรือ TypeScript เหมือนเขียนโปรแกรมทั่วไป แล้ว ORM จะแปลงคำสั่งเหล่านั้นให้เป็น SQL ที่ฐานข้อมูลเข้าใจเองโดยอัตโนมัติ
ทำไมโปรแกรมเมอร์ถึงนิยมใช้กัน เพราะมันช่วยลดความผิดพลาดในการพิมพ์คำสั่ง (Query) และช่วยให้เราจัดการความสัมพันธ์ระหว่างตารางข้อมูลได้ง่ายขึ้นมาก โดยเฉพาะในโปรเจกต์ขนาดใหญ่ที่ข้อมูลมีความเกี่ยวข้องกันหลายจุด การใช้เครื่องมืออย่าง Prisma หรือ TypeORM จะทำให้ชีวิตการเขียน Backend (ระบบหลังบ้าน) ของเราสะดวกและปลอดภัยขึ้นเยอะครับ
การตั้งค่า Schema เพื่อออกแบบโครงสร้างข้อมูล
ขั้นตอนแรกของการใช้ ORM คือการทำ Schema (พิมพ์เขียวหรือโครงสร้างของตารางข้อมูล) เราต้องกำหนดก่อนว่าในระบบของเราจะมีตารางอะไรบ้าง และแต่ละตารางเก็บข้อมูลประเภทไหน เช่น ตาราง User (ผู้ใช้) ต้องมีชื่อ อีเมล และรหัสผ่าน เป็นต้น
สมมติเราใช้ Prisma เราจะเขียนไฟล์ schema.prisma เพื่อกำหนดหน้าตาของฐานข้อมูล การเขียนแบบนี้ทำให้เรามองเห็นภาพรวมของข้อมูลได้ชัดเจนมาก ก่อนที่เราจะเริ่มสร้างฐานข้อมูลจริงขึ้นมาในเครื่องคอมพิวเตอร์ของเรา
ข้อควรระวังคือ อย่าลืมกำหนด Data Type (ประเภทของข้อมูล) ให้ถูกต้องตั้งแต่วันแรก เพราะถ้าเราเปลี่ยนใจทีหลังตอนมีข้อมูลจริงแล้ว การแก้ไขโครงสร้างจะยุ่งยากกว่าเดิมมากครับ
// ไฟล์ schema.prisma
model User {
id Int @id @default(autoincrement()) // สร้างไอดีอัตโนมัติ
email String @unique // อีเมลต้องไม่ซ้ำกัน
name String? // ชื่อเป็นค่าว่างได้
}
คำอธิบาย: บรรทัด model User คือการสร้างตารางชื่อ User, id คือคีย์หลัก, @unique คือการสั่งห้ามมีอีเมลซ้ำ, String? คือการบอกว่าช่องนี้เว้นว่างได้
ผลลัพธ์: เมื่อรันคำสั่งเตรียมการ ระบบจะสร้างไฟล์โครงสร้างที่พร้อมรอการแปลงเป็นตารางในฐานข้อมูลจริงบนเครื่องของคุณ
เข้าใจเรื่อง Migration การอัปเดตโครงสร้างฐานข้อมูล
เมื่อเราแก้ไฟล์ Schema แล้ว ฐานข้อมูลจริงๆ จะยังไม่เปลี่ยนตามทันที เราต้องใช้สิ่งที่เรียกว่า Migration (ขั้นตอนการบันทึกประวัติการเปลี่ยนแปลงฐานข้อมูล) เพื่อบอกให้ฐานข้อมูลอัปเดตตัวเองให้ตรงกับโค้ดที่เราเพิ่งเขียนไป
ลองนึกภาพว่าเรากำลังทำหนังสือพิมพ์ ถ้าเราแก้ข่าวในต้นฉบับแล้วไม่พิมพ์ใหม่ ข่าวเดิมก็ยังค้างอยู่ Migration ก็คือการสั่งพิมพ์หนังสือพิมพ์ฉบับใหม่ที่อัปเดตข้อมูลล่าสุดออกมานั่นเองครับ วิธีนี้ช่วยให้เราทำงานร่วมกับคนอื่นในทีมได้ง่าย เพราะทุกคนจะมีโครงสร้างฐานข้อมูลเหมือนกันหมด
จงจำไว้ว่าการทำ Migration คือการจัดการกับข้อมูลสำคัญ ห้ามลบหรือแก้ไขไฟล์ Migration ที่ถูกรันไปแล้วบนเซิร์ฟเวอร์จริง (เครื่องที่เปิดใช้งานให้คนอื่นเข้าถึง) โดยเด็ดขาด เพราะอาจทำให้ข้อมูลในระบบพังหรือสูญหายได้ครับ
# คำสั่งสั่งให้ Prisma สร้างตารางตาม Schema
npx prisma migrate dev --name init_database
คำอธิบาย: npx คือคำสั่งรันโปรแกรมในโหนด, prisma migrate dev คือคำสั่งอัปเดตฐานข้อมูล, --name คือการใส่ชื่อกำกับให้จำง่าย
ผลลัพธ์: โปรแกรมจะสร้างโฟลเดอร์ชื่อ migrations พร้อมไฟล์ SQL ที่ฐานข้อมูลจะนำไปใช้จริง และตาราง User จะถูกสร้างขึ้นในฐานข้อมูลของคุณ
การทำ CRUD Operations พื้นฐาน
CRUD คือหัวใจสำคัญของแอปพลิเคชันทุกตัว ย่อมาจาก Create (สร้าง), Read (อ่าน), Update (แก้ไข), และ Delete (ลบ) ข้อมูล ถ้าคุณเข้าใจสี่อย่างนี้ คุณก็สามารถสร้างระบบอะไรก็ได้บนโลกนี้แล้วครับ
การใช้ ORM ทำ CRUD นั้นง่ายมาก เพราะเราใช้แค่ฟังก์ชันในโค้ดแทนการเขียน SQL ยาวๆ ยกตัวอย่างเช่น ถ้าเราอยากเพิ่มผู้ใช้ใหม่ในฐานข้อมูล เราก็แค่สั่ง create โดยส่งข้อมูลเข้าไปเป็นก้อน (Object) เท่านั้นเองครับ
มือใหม่มักจะพลาดเรื่องการจัดการ Asynchronous (การทำงานแบบรอผลลัพธ์) อย่าลืมใส่ await ไว้หน้าคำสั่งฐานข้อมูลเสมอ เพราะการติดต่อฐานข้อมูลใช้เวลา ถ้าไม่รอให้เสร็จก่อน โค้ดบรรทัดถัดไปอาจจะทำงานผิดพลาดได้ครับ
// ตัวอย่างการเพิ่มข้อมูล (Create)
const newUser = await prisma.user.create({
data: {
name: 'Somchai',
email: 'somchai@example.com',
},
})
คำอธิบาย: prisma.user.create คือฟังก์ชันสั่งเพิ่มข้อมูล, data คือก้อนข้อมูลที่เราต้องการบันทึก, await คือการรอจนกว่าจะบันทึกเสร็จ
ผลลัพธ์: ข้อมูลชื่อ Somchai จะถูกเพิ่มลงในตาราง User พร้อมกับ ID ที่ระบบสร้างให้โดยอัตโนมัติ
การทำ CRUD ข้ามตาราง (Relational Data)
ความเก่งของ ORM คือการจัดการความสัมพันธ์ระหว่างตาราง เช่น ผู้ใช้หนึ่งคนมีโพสต์ได้หลายโพสต์ (One-to-Many) เราสามารถดึงข้อมูลผู้ใช้พร้อมกับโพสต์ของเขาออกมาได้ในคำสั่งเดียว ซึ่งถ้าเป็น SQL ปกติ เราต้องใช้การ Join (การนำตารางสองตารางมาเชื่อมกัน) ซึ่งซับซ้อนกว่ามาก
การเขียนโค้ดเพื่อดึงข้อมูลข้ามตาราง เราจะใช้คำสั่ง include เพื่อบอก ORM ว่าต้องการดึงตารางไหนมาพ่วงด้วย สิ่งนี้ช่วยให้โค้ดของเราสะอาดและอ่านง่ายขึ้นมากเมื่อเทียบกับการเขียน SQL แบบดั้งเดิม
ข้อควรระวังคือ อย่าดึงข้อมูลข้ามตารางมากเกินความจำเป็น เช่น ถ้าตารางหนึ่งมีข้อมูลเป็นล้านแถว การดึงข้อมูลพ่วงทั้งหมดมาจะทำให้ระบบช้าลงทันที ให้ดึงมาเฉพาะส่วนที่ต้องใช้แสดงผลจริงเท่านั้นครับ
// ดึงข้อมูล User พร้อมกับโพสต์ (Post)
const userWithPosts = await prisma.user.findUnique({
where: { id: 1 },
include: { posts: true }
})
คำอธิบาย: findUnique คือการหาข้อมูลตามไอดี, include คือการสั่งให้ดึงข้อมูลตาราง posts ที่สัมพันธ์กันออกมาด้วย
ผลลัพธ์: คุณจะได้ข้อมูล User ที่มีอาร์เรย์ของ posts แทรกอยู่ข้างใน ทำให้แสดงผลข้อมูลที่เกี่ยวข้องกันได้ทันที
สรุป: เริ่มต้นพัฒนาด้วย ORM อย่างมืออาชีพ
การใช้ ORM ไม่ใช่แค่ทางลัด แต่เป็นมาตรฐานที่โปรแกรมเมอร์ยุคใหม่ต้องมีติดตัวไว้ มันช่วยให้เราโฟกัสที่การออกแบบ Logic (ตรรกะหรือเหตุผลของโปรแกรม) ของแอปพลิเคชันได้มากกว่าการนั่งแก้บั๊ก SQL ที่ซับซ้อน
หากคุณกำลังทำโปรเจกต์แรก แนะนำให้ลองเริ่มจาก Prisma เพราะมี Documentation (เอกสารอธิบายการใช้งาน) ที่ดีมากและเหมาะกับมือใหม่ที่สุด หลังจากเข้าใจพื้นฐานแล้ว การขยับไปใช้ TypeORM หรือเครื่องมืออื่นในอนาคตก็จะกลายเป็นเรื่องง่าย เพราะแนวคิดหลักของ ORM ทุกตัวเหมือนกันหมดครับ
สุดท้ายนี้ อย่าลืมลองฝึกทำโปรเจกต์เล็กๆ เช่น ระบบสมุดโทรศัพท์หรือระบบจดบันทึกส่วนตัว โดยใช้ ORM ในการจัดการข้อมูลทั้งหมด การลงมือทำจริงจะทำให้คุณเห็นภาพว่า Schema, Migration และ CRUD ทำงานสอดประสานกันอย่างไรในสถานการณ์จริงครับ