Logo

dev-resources.site

for different kinds of informations.

ตัวอย่างการสร้าง Many to Many Association ผ่าน GORM

Published at
6/7/2020
Categories
go
gorm
Author
iporsut
Categories
2 categories in total
go
open
gorm
open
Author
7 person written this
iporsut
open
ตัวอย่างการสร้าง Many to Many Association ผ่าน GORM

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)
);
Enter fullscreen mode Exit fullscreen mode

ในมุมของ 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"`
}
Enter fullscreen mode Exit fullscreen mode

ในตัวอย่างนี้เราก็มีอีกคู่ที่เป็น 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)
);
Enter fullscreen mode Exit fullscreen mode

แล้วใน 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"`
}
Enter fullscreen mode Exit fullscreen mode

สุดท้ายเมื่อเรา 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)
Enter fullscreen mode Exit fullscreen mode

โค้ด 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;
Enter fullscreen mode Exit fullscreen mode

โค้ด 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)
}
Enter fullscreen mode Exit fullscreen mode

หรือจะ clone ไปลองเล่นได้ทีนี่ https://github.com/iporsut/example-gorm-many-to-many

gorm Article's
26 articles in total
Favicon
Gorm: Sneak Peek of Custom Data Types
Favicon
GORM, PostgreSQL & Atlas
Favicon
Gorm Pagination With Ease
Favicon
How to Use Arrays as Parameters in Golang?
Favicon
Basic CRUD Operations Using Golang, Gin Gonic, and GORM
Favicon
GORM and Goose Migrations
Favicon
Gin + Gorm Practical Guide, Implementing a Simple Q&A Community Backend Service in One Hour
Favicon
A secret weapon to improve the efficiency of golang development, a community backend service was developed in one day
Favicon
Building CRUD Operations in Golang 🎉
Favicon
Some GORM Tips and Notes
Favicon
Gorm level UP: how to upgrade and start to use Gorm v2
Favicon
5chan API - Golang, GORM, Go-fiber
Favicon
Pagination using Gorm scopes
Favicon
GOlang URL shortener service using postgres, redis, bulma
Favicon
RestFull API Detailing
Favicon
Daily Blog
Favicon
Rest API Database Blog Post
Favicon
Prevent updating query updates all records in GORM
Favicon
Init project
Favicon
Creating a complete golang rest api from zero to Hero
Favicon
ตัวอย่างการสร้าง Many to Many Association ผ่าน GORM
Favicon
DataBinding and GORM objects
Favicon
Creating an opinionated GraphQL server with Go - Part 4
Favicon
Creating an opinionated GraphQL server with Go - Part 3
Favicon
Grails: Mocking domain objects/criteria that reference Joda Time objects
Favicon
Go's ORM framework GORM best practices indefinite parameter usage

Featured ones: