技術メモ

神奈川在住のITエンジニアの備忘録。おもにプログラミングやネットワーク技術について、学んだことを自分の中で整理するためにゆるゆると書いています。ちゃんと検証できていない部分もあるのでご参考程度となりますが、誰かのお役に立てれば幸いです。

gorm を使って postgres にアクセス

golang のコードから gorm を使って postgres にアクセスして、CRUD を行う時の実装例をメモとして残しておく。
実行環境は以下の通り。

gorm のインストールは以下で実施した。

go get -u gorm.io/gorm
go get -u gorm.io/driver/postgres

ソースは以下の通り。CRUD をそれぞれ実行している。
注意点などはソース中にコメントに書いた。

package main

import (
	"fmt"
	"time"

	"gorm.io/driver/postgres"
	"gorm.io/gorm"
)

type Product struct {
	// メンバー変数名の最初が大文字でないと、gorm で postgres から値が取れない。
	// 中身が初期値の構造体が返るだけになる。
	// 構造体にタグを付けて、テーブルのカラム名との対応を取る。
	Name        string    `gorm:"column:name"`
	Price       int       `gorm:"column:price"`
	LastUpdated time.Time `gorm:"column:last_updated"`
}

// テーブル名と Product 構造体を紐付ける。
func (Product) TableName() string {
	return "product"
}

func main() {
	db, err := getConn()
	if err != nil {
		panic(err)
	}

	// Creat
	fmt.Println("--- Insert rows ---")
	curTime := time.Now()
	err = createProducts(db, Product{Name: "prod1", Price: 1000, LastUpdated: curTime})
	if err != nil {
		panic(err)
	}
	err = createProducts(db, Product{Name: "prod2", Price: 2000, LastUpdated: curTime})
	if err != nil {
		panic(err)
	}

	// Read
	fmt.Println("--- Select all ---")
	result, err := getAllProducts(db)
	if err != nil {
		panic(err)
	}
	printProducts(result)

	fmt.Println("--- Select one row ---")
	oneResult, err := getProduct(db, "prod1")
	if err != nil {
		panic(err)
	}
	printProducts(&[]Product{*oneResult})

	// Update
	err = updateProducts(db, "prod1", time.Now())
	if err != nil {
		panic(err)
	}

	// Upadte 結果確認
	fmt.Println("--- After update ---")
	result, err = getAllProducts(db)
	if err != nil {
		panic(err)
	}
	printProducts(result)

	// Delete
	err = deleteProducts(db, "prod1")
	if err != nil {
		panic(err)
	}
	err = deleteProducts(db, "prod2")
	if err != nil {
		panic(err)
	}

	// Delete 結果確認
	fmt.Println("--- After delete ---")
	result, err = getAllProducts(db)
	if err != nil {
		panic(err)
	}
	printProducts(result)
}

func createProducts(db *gorm.DB, product Product) error {
	err := db.Create(product).Error
	if err != nil {
		return err
	}
	return nil
}

func getAllProducts(db *gorm.DB) (*[]Product, error) {
	var products []Product
	err := db.Find(&products).Error
	if err != nil {
		return nil, err
	}
	// 結果の構造体のサイズが大きくなる場合に備えて参照で返す。(使用メモリ量の節約のため)
	return &products, nil
}

func getProduct(db *gorm.DB, name string) (*Product, error) {
	var product Product
	err := db.Where("name = ?", name).Find(&product).Error
	if err != nil {
		return nil, err
	}
	return &product, nil
}

func updateProducts(db *gorm.DB, name string, curTime time.Time) error {
	err := db.Model(&Product{}).Where("name = ?", name).Update("last_updated", curTime).Error
	if err != nil {
		return err
	}
	return nil
}

func deleteProducts(db *gorm.DB, name string) error {
	err := db.Where("name = ?", name).Delete(Product{}).Error
	if err != nil {
		return err
	}
	return nil
}

// 引数の構造体のサイズが大きくなった時に備えて参照渡しとする。
func printProducts(products *[]Product) {
	for _, product := range *products {
		fmt.Printf("name: %s, price: %d, lastUpdated: %s\n",
			product.Name, product.Price, product.LastUpdated)
	}
}

func getConn() (*gorm.DB, error) {
	dsn := "host=localhost user=postgres password=psgpass dbname=postgres port=5432 sslmode=disable TimeZone=Asia/Tokyo"
	db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
	return db, err
}

これを実行すると、以下が出力される。

--- Insert rows ---
--- Select all ---
name: prod1, price: 1000, lastUpdated: 2022-02-13 18:00:26.862434 +0000 UTC
name: prod2, price: 2000, lastUpdated: 2022-02-13 18:00:26.862434 +0000 UTC
--- Select one row ---
name: prod1, price: 1000, lastUpdated: 2022-02-13 18:00:26.862434 +0000 UTC
--- After update ---
name: prod2, price: 2000, lastUpdated: 2022-02-13 18:00:26.862434 +0000 UTC
name: prod1, price: 1000, lastUpdated: 2022-02-13 18:00:26.914389 +0000 UTC
--- After delete ---