ต่อจากจาก entry นี้ครับ มาดูการจัดการ GORM Relationship แบบ 1 ต่อ 1

มาดูต่อไปว่าจะจัดการความสัมพันธ์แบบ one-to-many ได้อย่างไร โดยผมจะใช้ตัวอย่างจาก
สนุกกับ Active Record ภาค Grails : ค้น, ค้น, ค้น

มาเริ่มกันเลยครับ … go go go !!!!

กำหนดให้ Domain class 2 ตัวคือ
- Writer เก็บข้อมูลของนักเขียน
- Book เก้บข้อมูลของหนังสือ

Business Rule
- หนังสือ 1 เล่มสามารถมีผู้แต่งได้เพียง 1 คน
- นักเขียนสามารถเขียนหนังสือได้มากว่า 1 เล่ม

ถ้ายังไม่ทำการสร้าง domain class ให้สร้างดังนี้
$grails create-domain-class Writer
$grails create-domain-class Book

และทำการแก้ไขดังนี้

/domains/Writer.groovy

class Writer {
	String firstname
	String lastname
}

/domains/Book.groovy

class Book {
	String title;
	Integer pages;
	Date published_at;
}

ต่อไปทำการกำหนดความสัมพันธ์ของทั้งสอง Domain classes แบบ one-to-many

1. Unidirectional หรือแบบทางเดียวทำได้ง่ายมาก ซึ่งใช้ hasMany ในการกำหนด ดังนี้
- นักเขียนจะรู้ว่าแต่งหนังสืออะไรบ้างเท่านั้น

/domains/Writer.groovy

class Writer {
	static hasMany = [ books : Book ]   // ชนิดของ books คือ java.util.Set
	String firstname
	String lastname
}

เท่านี้ก็สามารถทำการสร้างความสัมพันธ์แบบ one-to-many [ Unidirectional ] ได้แล้วครับ แต่จะไม่ทำการ cascading เมื่อมีการ create, update, delete นะครับ

สามารถแก้ปัญหาข้างต้นด้วยการเพิ่ม belongsTo แบบ Unidirectional นะครับ ซึ่งทำการเพิ่มใน /domains/Book.groovy ดังนี้

class Book {
	static belongsTo = Writer   // กำหนดว่าใครเป็นเจ้าของหรือคนเขียนหนังสือเล่นนี้ขึ้นมา แบบ Unidirectional
	String title;
	Integer pages;
	Date published_at;
}

ส่วนการใช้งานนั้นไม่ยากเช่นกันครับ เริ่มที่สร้างข้อมูล

import Writer
def w = new Writer( firstname:"Fname", lastname:"Lname" )
def b1 = new Book( title:"B1", pages:100, published_at:new Date() )
def b2 = new Book( title:"B2", pages:200, published_at:new Date() )
def b3 = new Book( title:"B3", pages:300, published_at:new Date() )
w.books = [ b1, b2, b3 ]
w.save()

ส่วนการดูข้อมูลก็ลักษณะเดิมครับ แต่จะเพิ่มส่วนการแสดงผล list ของ Book เท่านั้นเอง

import Writer
def w = Writer.get(2)
w.books.each {
    println it.id + " : " + it.title
}

ลองทำการลบ Writer แล้วจะลบข้อมูลทั้งยวง คือลบทั้ง Writer และ Book ที่เกี่ยวข้องครับ ดังนี้

import Writer
def w = Writer.get(1)
w.delete()

2. Bidirectional หรือแบบสองทาง
- นักเขียนจะรู้ว่าแต่งหนังสืออะไรบ้างเท่านั้น และในทางตรงข้ามหนังสือก็รู้ว่าใครแต่งหรือเขียนมา
นั้นจะใช้ belongsTo เพื่อมาระบุว่าใครคือเจ้าของ จากตัวอย่าง เราจะเอาใช้ระบุว่า ใครเป็นผู้เขียนหนังสือในเล่มนั้น ดังนี้

/domains/Writer.groovy

class Writer {
	static hasMany = [ books : Book ]   // ชนิดของ books คือ java.util.Set
	String firstname
	String lastname
}

/domains/Book.groovy

class Book {
	static belongsTo = [ writer : Writer ]  // กำหนดว่าใครเป็นเจ้าของหรือคนเขียนหนังสือเล่นนี้ขึ้นมา แบบ Bidirectional
	String title;
	Integer pages;
	Date published_at;
}

3. มาดูกรณีพิเศษอีก 2 แบบครับ [ อาจจะดูซับซ้อนไปหน่อยนะครับ แต่รู้ไว้ใช่ว่าครับ .... บางทีอาจจะไม่ได้ใช้ซะอีก ] จะมีการใช้ mappedBy มาช่วยดังนี้
กำหนดให้ domain class Book มี writer สองชนิดดังนี้

/domains/Book.groovy

class Book {
	Writer writer_type_1
	Writer writer_type_2
	String title;
	Integer pages;
	Date published_at;
}

คำถามที่เกิดขึ้นมาคือ จะกำหนดความสัมพันธ์กับ writer ตัวไหนล่ะ ????
คำตอบ ใช้ mappedBy มากำหนด ตัวอย่างกำหนดให้สัมพันธ์กับ writer_type_2 จะสามารถกำหนดได้ดังนี้
/domains/Writer.groovy

class Writer {
	static hasMany = [ books : Book ]   // ชนิดของ books คือ java.util.Set
	static mappedBy = [ books : "writer_type_2" ] //  map กับ property writer_type_2 ใน domain class Book
	String firstname
	String lastname
}

หรือต้องการซับซ้อนเข้าไปอีก ดังตัวอย่าง
/domains/Writer.groovy

class Writer {
	static hasMany = [ books_type_1: Book,  books_type_2:Book ]
	static mappedBy = [ books_type_1:"writer_type_1", books_type_2:"writer_type_2" ]
	String firstname
	String lastname
}

เขาจะเรียกการกำหนดแบบนี้ว่า Muiltiple Collection ครับ ซึ่งมันซับซ้อนมากขึ้นเรื่อยๆ ครับ แต่ก็เป็นไปตาม business rule ของแต่ละระบบครับ ว่าต้องการอย่างไรบ้าง

นี่ก็เป็นการกำหนดความสัมพันธ์แบบ one-to-many อย่างคร่าวๆ ครับผม โดยน่าจะใช้กันมากถึงมากที่สุดในระบบแล้วครับ

Enjoy with Grails …. ครับ

Tags: , ,

7 Responses to “มาดูการจัดการ GORM Relationship แบบ One-to-Many”

  1. one-to-many มาแล้ว !! ขอบคุณครับ
    ปล. @up1 go go go นี่ SCV เหรอครับ

  2. ตรง mappedBy นี่ร้ายกาจมาก ๆ ครับ

  3. @jittat จะสวยกว่านี้ถ้าใน Groovy มี symbol ครับ

  4. Unidirectional กับ Bidirectional นี่ต่างกันแค่ตรง

    static belongsTo = Writer
    กับ
    static belongsTo = [ writer : Writer ]

    เท่านั้นใช่มั้ยครับ เท่าที่ดู
    แต่เข้าใจว่า GORM จะสร้าง schema ออกมาเหมือนกันอยู่แล้วไม่ว่าแบบไหน รึเปล่าครับ

  5. 1. ถ้าใช้ belogsTo ก็ใช่ครับ แตกต่างกันตรงนี้ ใช้เมื่อคุณต้องการให้ระบบ support cascading ครับ

    2. schema เหมือนกันครับ แต่ใน layer ของ persistence จะต่างกัน [ Hibernate Configuration ต่างกันนั่นเองครับ ]

  6. ขอบคุณครับ :D

Trackbacks/Pingbacks

  1. เกรลส์ หกสิบหก » การจัดการ GORM Relationship แบบ Many-to-Many

Leave a Reply