GORM เป็น Go ORM ช่วยให้เรา Map ระหว่างโครงสร้างของ Column ใน Table ของ Database กับ Field ของ Struct ใน Go ได้
ตัวอย่างโพสต์นี้เราจะให้เห็นการ Map ของโครงสร้างที่มีความสัมพันธ์กันแบบ Many to Many เช่นเรามี table actors
และมี table films
ซึ่งแต่ละ record ของ actors
จะโยงกับ films
ได้หลายเรื่อง และ films
เองก็โยงกับ actors
ได้หลาย actors
ซึ่งการเก็บข้อมูลสำหรับความสัมพันธ์แบบนี้ เราจะสร้างอีก table เพื่อโยง primary key ของ actors
กับ primary key ของ films
เข้าด้วยกัน ดังนี้
CREATE TABLE IF NOT EXISTS actor_films(
actor_id INTEGER NOT NULL REFERENCES actors(id) ON DELETE CASCADE,
film_id INTEGER NOT NULL REFERENCES films(id) ON DELETE CASCADE,
PRIMARY KEY (actor_id, film_id)
);
ในมุมของ struct ใน Go เราจะสร้าง struct Actor
และมี field Films
เพื่อเก็บ list ของ Film
ที่โยงกับ Actor
แล้วเราจะใช้ gorm:"many2many:actor_films;"
struct tag เพื่อบอกว่า Fields นี้จะถูกโหลดข้อมูลของ Film โดยอาศัยความสัมพันธ์ผ่านตาราง actor_films
นั่นเอง
type Actor struct {
ID uint `json:"id"`
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
Films []Film `json:"films" gorm:"many2many:actor_films;"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
ในตัวอย่างนี้เราก็มีอีกคู่ที่เป็น Many to Many นั่นคือ films
กับ categories
เราก็อาศัย table เพื่อโยงความสัมพันธ์เช่นกันชื่อ film_catagories
CREATE TABLE IF NOT EXISTS film_categories(
film_id INTEGER NOT NULL REFERENCES films(id) ON DELETE CASCADE,
category_id INTEGER NOT NULL REFERENCES categories(id) ON DELETE CASCADE,
PRIMARY KEY (film_id, category_id)
);
แล้วใน Film
struct เราก็กำหนด gorm struct tag แบบนี้
type Film struct {
ID uint `json:"id"`
Title string `json:"title"`
LanguageID uint `json:"-"`
Language Language `json:"language"`
Categories []Category `json:"categories" gorm:"many2many:film_categories;"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
สุดท้ายเมื่อเรา map field โดยอาศัย gorm struct tag แล้วเราสามารถใช้ method Preload
เพื่อสั่งใช้ query association field อย่าง Films
และ Categories
หรือ Language
ของ Film
ก็ได้ เช่น
var dest []Actor
db.Preload("Films.Language").Preload("Films.Categories").Find(&dest)
json.NewEncoder(os.Stdout).Encode(&dest)
โค้ด table ทั้งหมดในตัวอย่าง
BEGIN;
CREATE TABLE IF NOT EXISTS actors(
id SERIAL NOT NULL PRIMARY KEY,
created_at TIMESTAMPTZ NOT NULL,
updated_at TIMESTAMPTZ NOT NULL,
first_name CHARACTER VARYING,
last_name CHARACTER VARYING
);
CREATE TABLE IF NOT EXISTS languages(
id SERIAL NOT NULL PRIMARY KEY,
created_at TIMESTAMPTZ NOT NULL,
updated_at TIMESTAMPTZ NOT NULL,
name CHARACTER VARYING
);
CREATE TABLE IF NOT EXISTS films(
id SERIAL NOT NULL PRIMARY KEY,
created_at TIMESTAMPTZ NOT NULL,
updated_at TIMESTAMPTZ NOT NULL,
title CHARACTER VARYING,
language_id INTEGER NOT NULL REFERENCES languages(id)
);
CREATE TABLE IF NOT EXISTS categories(
id SERIAL NOT NULL PRIMARY KEY,
created_at TIMESTAMPTZ NOT NULL,
updated_at TIMESTAMPTZ NOT NULL,
name CHARACTER VARYING
);
CREATE TABLE IF NOT EXISTS film_categories(
film_id INTEGER NOT NULL REFERENCES films(id) ON DELETE CASCADE,
category_id INTEGER NOT NULL REFERENCES categories(id) ON DELETE CASCADE,
PRIMARY KEY (film_id, category_id)
);
CREATE TABLE IF NOT EXISTS actor_films(
actor_id INTEGER NOT NULL REFERENCES actors(id) ON DELETE CASCADE,
film_id INTEGER NOT NULL REFERENCES films(id) ON DELETE CASCADE,
PRIMARY KEY (actor_id, film_id)
);
COMMIT;
โค้ด Go ที่ใช้ GORM ช่วย map field
package main
import (
"encoding/json"
"log"
"os"
"time"
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/postgres"
)
type Actor struct {
ID uint `json:"id"`
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
Films []Film `json:"films" gorm:"many2many:actor_films;"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
type Film struct {
ID uint `json:"id"`
Title string `json:"title"`
LanguageID uint `json:"-"`
Language Language `json:"language"`
Categories []Category `json:"categories" gorm:"many2many:film_categories;"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
type Language struct {
ID uint `json:"id"`
Name string `json:"name"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
type Category struct {
ID uint `json:"id"`
Name string `json:"name"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func initialData(db *gorm.DB) {
tx := db.Begin()
defer tx.Commit()
lang := Language{
Name: "English",
}
tx.Create(&lang)
cate := Category{
Name: "Documentary",
}
tx.Create(&cate)
film := Film{
Title: "Young Language",
Language: lang,
Categories: []Category{cate},
}
tx.Create(&film)
actor := Actor{
FirstName: "Ed",
LastName: "Chase",
Films: []Film{film},
}
tx.Create(&actor)
}
func main() {
connURL := os.Getenv("POSTGRESQL_URL")
log.Println(connURL)
db, err := gorm.Open("postgres", connURL)
if err != nil {
panic("failed to connect database")
}
defer db.Close()
initialData(db)
var dest []Actor
db.Preload("Films.Language").Preload("Films.Categories").Find(&dest)
json.NewEncoder(os.Stdout).Encode(&dest)
}
หรือจะ clone ไปลองเล่นได้ทีนี่ https://github.com/iporsut/example-gorm-many-to-many
Top comments (0)