O/R Mapping : Data Access Object

เกริ่นนำ

    ในตอนที่แล้ว ผมแนะนำพื้นฐานของ Object และ Relational Database พูดถึงความเข้ากันไม่ได้ มีเสียงตอบรับกลับมาแบ่งได้เป็นสองขั้ว ขั้วแรกก็ดูเหมือนว่าจะชอบมาก อยากให้เขียนตอนต่อไปให้เร็วๆ ส่วนอีกขั้วหนึ่งก็บ่นออกมาว่าไม่รู้เรื่อง ไม่เข้าใจว่าผมต้องการสื่ออะไร อาจจะเป็นเพราะผมเขียนแล้วอ่านไม่รู้เรื่อง หรือไม่ก็เรื่องมันอาจจะใหม่มากจนตีลังกาคิดตามไม่ทัน ถ้าเป็นข้อแรก ก็สุดความสามารถแล้วครับ คงต้องโทษผมเองว่าภาษาไทยไม่ดี ไม่มีความสามารถในการสื่อสารที่ดีพอ แต่ถ้าเป็นข้อหลัง ก็คงพอแก้กันได้ วันนี้ผมเลยเอาตัวอย่างมาให้ดูกัน น่าจะทำความเข้าใจได้โดยไม่ยากนัก

าเริ่มกันที่ Object

    เวลาเราออกแบบโปรแกรมโดยใช้ Data Flow Diagramเป็นหลัก ผลลัพธ์ที่ตกผลึกก็คือ Table ซึ่งมักจะอยู่ในรูปของ E-R Diagram แต่ถ้าเราใช้ Methodology ใดๆ ที่ยึดเอา Object-Oriented เป็นหลัก เช่น Extreme Programming, Unified Process หรือ ICONIX เป็นอาทิ ผลลัพธ์ปลายทางมักจะเป็น Class Diagram (เริ่มต้นอาจจะเป็น Use Case Diagram สำหรับ Unified Process) สมมุติว่าผมทำตามกระบวนการแล้วได้ class อยู่ class หนึ่ง ชื่อ Book ก็แล้วกัน ข้างในเก็บ รหัสหนังสือและชื่อเรื่อง เอากันแบบง่ายๆ นี้ก่อน เราจะได้ Class Diagram ของ UML เป็นแบบนี้ครับ (ไม่รู้ว่าใน Visio สร้าง property อย่างไร เลยเขียนเป็นแบบ Get/Set แบบ Java เสียเลย)

ตามมาด้วย Table

    ส่วนในฐานข้อมูลเราก็มี Table ที่ชื่อว่า book ที่มีโครงสร้างแบบนี้

    จะเห็นได้ว่าเรามี 2 โลก โลกใบแรก เราออกแบบโดยยึดหลักการ ข้างบนลงข้างล่างนั้นคือโลกของ Object-Oriented ซึ่งผลลัพธ์ก็คือ Class Book ส่วนอีกโลกหนึ่งออกแบบจากข้างล่างขึ้นข้างบนนั่นก็คือโลกของ Relational Database ซึ่งผลลัพธ์ก็คือ Table book นั่นเอง

    มีคำถามอยู่คำถามหนึ่งที่มักจะถามกัน ในการออกแบบโปรแกรมนั้น โลกไหนเกิดก่อน ผมคงไม่ตอบว่า แล้วแต่ถนัดหรอกครับ มันต้อง Class มาก่อนเสมอ ถึงจะเรียกได้ว่าเป็น Object-Oriented แต่ถ้า Table มาก่อนมันจะเป็น Data-Driven ไป แต่ถ้าอันไหนก่อนก็ได้ แบบนี้เรียกว่าไม่มี Methodology เลยครับ

    เมื่อเราจะใช้ Object-Oriented แล้ว เราต้องถือโลกของ Object เป็นหลัก ในกรณีนี้เราสร้าง class Book ขึ้นมา ส่วน Table book ที่สร้างขึ้นมานั้น ก็สร้างขึ้นเพื่อรองรับ class Book นั่นเอง ดังนั้นจึงไม่แปลกใจเลยว่าทำไม เนื้อหาของทั้งสองอย่างจึงคล้ายกันหรืออาจจะเรียกได้ว่าเหมือนกันก็ว่าได้

จากนั้นก็ Data Access Object (DAO)

    ทีนี้เรามาดูความต้องการกัน เมื่อเราออกแบบโดยใช้ OO แล้ว เราก็ไม่อยากยุ่งกับ Relational Database หรือ SQL อีก ถ้าเรา new object Book ขึ้นมา ใส่ id และ ชื่อหนังสือลงไป คราวนี้ เราอยากจะเนรมิตให้ข้อมูลที่เราใส่เข้าไปใน Object นั้น ให้มันไปโผล่ในฐานข้อมูล Table book เลย ถ้าทำได้โดยที่เราไม่ต้องยุ่งกับ SQL เลยก็สุดยอดครับ เราจะได้ออกแบบโดยใช้ OO แบบเต็มรูป ไม่ต้องบิดโปรแกรมเพื่อให้สามารถจัดการกับ Relational Database  และนั่นคือที่มาของ Layer ที่ชื่อว่า O/R Mapping นั่นเอง เรามาดูผู้ช่วยในการทำ O/R Mapping กัน

    Class นี้เป็น Class ที่รับอาสาเป็นนายหน้าจัดการฐานข้อมูลให้กับ Book มันก็เลยได้ชื่อว่า BookDAO class นี้ควรจะเป็น Abstract class และมี parameter ทุกตัวเป็น static หรือไม่ก็ทำอย่างไรก็ได้ให้มัน new ได้ครั้งเดียว (ลองศึกษา Design Pattern ตัวที่ชื่อว่า Singleton) ทีนี้ถ้าเราต้องการสร้างบันทึก เราเขียน Code แบบนี้ครับ

Book book1 = new Book1();
book1.SetId("a001");
book1.SetName("พันธุ์หมาบ้า");
BookDAO.Insert(book1);

    แค่นี้เองครับ คงไม่ต้องเดาต่อนะครับใน Insert() นั้น มันจะแกะเอาข้อมูลใน Object ออกมา ตัดต่อเป็นคำสั่ง SQL Insert จากนั้นก็ยิงไปที่ SQL Server แต่ถ้าว่ากันไปแล้ว คนที่เขียน Code ข้างบน ก็ไม่จำเป็นต้องรู้ว่าภายใน Insert() นั้นทำงานอะไร เขาเพียงแค่รู้ว่าต้องเรียกคำสั่ง BookDAO.Insert() มาใช้ ในมุมมองของคนที่เขียน Code นี้จึงไม่ต้องรับรู้เรื่องเกี่ยวกับ Relational Database แต่อย่างใด เขาคิดแต่เพียงว่าเมื่อเรียกคำสั่งแล้ว มันจะเอา Object ที่สร้างขึ้นมานั้น ไปเก็บไว้ที่ใดที่หนึ่ง ปิดเครื่องก็ไม่หายไป ซึ่งเรียกว่า Persistent นั่นเอง

    คุณอาจแย้งออกมาว่า เราไม่ต้องยุ่งกับ SQL ตรงนี้ก็จริง แต่ใน BookDAO อย่างไรเสียก็คงยังต้องยุ่งกับ SQL อยู่ดี ไม่เห็นมันจะดีตรงไหนเลย ถ้าคุณคิดอย่างนี้ ลองพิจารณาดูนะครับ ด้วยวิธีข้างต้น คำสั่ง SQL นั้นจะถูกจำกัดพื้นที่ให้อยู่ใน class ...DAO หมด ไม่หลงเหลือออกมาข้างนอกเลย ถ้าเราให้โปรแกรมเมอร์คนเดียวจัดการ class ...DAO โปรแกรมเมอร์คนอื่นก็ไม่จำเป็นต้องเขียน SQL เป็นด้วยซ้ำ (ถ้าคุณไม่ใช้ DAO แต่ใช้เครื่องมือช่วยอื่นๆ ที่ผมจะนำเสนอต่อไปในบทต่อๆ ไป คุณไม่ต้องยุ่งใดๆ กับ SQL เลย)

    ผมเคยเขียนบอกแล้วว่าคำสั่ง SQL เขียนมากๆ มันจะก็ปัญหา รวบให้มันอยู่ในที่กักกันเฉพาะจะดีกว่า ถ้าจะเปลี่ยนอะไร ก็คงไม่ต้องไปตามเปลี่ยนทั้งโปรแกรม เปลี่ยนแค่ที่ Layer นี้ก็พอ Layer ที่จัดการ Relational Database โดยเฉพาะ Layer อื่นต้องไม่ติดต่อใดๆ กับ Relational Database เลย

    ความสามารถหนึ่งทื่ Microsoft จะเพิ่มเติมขึ้นมาใน ASP.NET 2.0 นั้นคือการเอาคำสั่ง SQL ไปเขียนที่ Presentation Layer เลย แบบนี้ยอมรับไม่ได้จริงๆ ถ้าคุณเป็นโปรแกรมเมอร์ที่เขียนงานขนาดใหญ่หน่อย เป็นกฏเลยครับว่าห้ามใช้ 

    ส่วนคำสั่ง Update() และ Delete() ก็คงไม่ต้องอธิบายกันเพิ่มเติม มันเหมือนกับ Insert() เลยครับ เรามาดูให้ลึกกันถึง คำสั่ง Find...() เลยดีกว่า เอาที่ FindById() น่าจะง่าย คำสั่งนี้ผู้ใช้ต้องส่งรหัส id เข้าไป เพื่อให้ได้ Object ออกมา คงไม่ต้องบอกนะครับ ว่าข้างในมันต้องตัดต่อคำสั่ง SQL Select อย่างแน่นอน แต่มีประเด็นที่น่าพูดถึงครับ มันจะ return object ออกมาได้อย่างไร เรามาดูกัน เมื่อมันตัดต่อคำสั่ง Select แล้ว มันก็จะยิงไปที่ SQL Server เพื่อให้ได้ข้อมูลกลับมา พอมีข้อมูลแล้ว มันจะ แอบ new Object จาก class Book ได้ออกมาเป็น Object เปล่าๆ ที่ไม่มีข้อมูล จากนั้นมันก็เอาข้อมูลที่ดึงมาได้จาก SQL Server บรรจุเข้าไปใน Object โดยผ่านคำสั่ง SetId() และ SetName() ของ object Book นั่นเอง กรรมวิธีการบรรจุนี้ฝรั่งมองว่ามันเหมือนกับการฉีดยาครับ เขามองว่าการกระทำอย่างนี้คือการฉีดข้อมูลเข้าไปยัง Object ดังกล่าว ศัพท์ฝรั่งเลยเรียกการทำอย่างนี้ว่า "Injection" (เหมือนกับเรื่องหัวฉีด Direct Injection ให้โฆษณารถกระบะ)

    FindByName() ก็ใส่แค่บางส่วนของ String คงไม่ต้องเดามาก เวลา SQL Select ก็สร้างในส่วน Where เป็น like แล้วคร่อม ข้อมูลที่ต้องการค้นหาด้วย % เท่านั้นเอง แต่คำสั่งนี้ได้ข้อมูลออกมาหลาย Object เราก็ต้องวนสร้าง Object แล้ว Inject ข้อมูลเข้าไป จากนั้นเอาไปใส่ใน ArrayList แล้วก็ ส่ง ArrayList นั้นกลับมา มันก็เท่านั้น

    ทั้งหมดนี้เราก็จะได้ตัวช่วยในการจัดการ Relational Database นั้นก็คือ O/R Mapping Layer โดยการใช้ DAO ซึ่ง มันจะเป็นฉนวนป้องกันโปรแกรมส่วนอื่น ไม่ให้ต้องรับรู้เรื่องเกี่ยวกับ Relational Database ครับ

ทิ้งท้าย

    วิธีนี้เป็นวิธีที่เข้าใจง่าย น่าจะเป็นทางลัดที่สุดแล้วสำหรับผู้ที่ต้องการเรียนรู้ว่า O/R Mapping มันคืออะไรกันแน่ หว้งว่าคงจะกระจ่างแล้วนะครับ แต่บอกไว้ก่อนนะครับ วิธีนี้เป็นวิธีการพื้นฐานเท่านั้น ถ้าเอาไปใช้งานจริง จะมีปัญหามาก เพื่อให้สั้นกระชับผมคงยังไม่แจกแจงปัญหาให้เห็นมาก (กลัวว่าจะเขียนไม่เสร็จสักที) เก็บเอาไว้ตอนหน้าดีกว่า แต่อย่างน้อยๆ เราก็เห็นแล้วว่า ถ้าเรามี 20 fields เราต้องสร้าง Method Find..() มากเป็นเงาตามตัว เผลอๆ อาจจะต้องมี ทำนองว่า FindSalaryBetween(int min, int max) อะไรทำนองนี้ ซึ่งทำให้เขียนแล้ววุ่นวายมาก

    ทิ้งท้ายให้ว่า DAO นั้นทาง Sun นำเอาไปเรียบเรียงใหม่ เพิ่มความสามารถและความยืดหยุ่นขึ้น เช่นให้มันอิสระจากฐานข้อมูลยี่ห้อต่างๆ ผลลัพธ์ออกมาเป็น Design Pattern ตัวหนึ่งที่ชื่อว่า Data Access Object ก็ชื่อเดิมนั่นแหละครับ DAO ศึกษาเพิ่มเติมได้จากที่นี่ครับ

17/May/05