ORM(Object Relational Mapping)というのは、SQL文ではなくオブジェクト指向でデータベースを操作できる機能です。
Go言語ではGORMがもっともポピュラーなようなので、見てみましょう。
https://gorm.io/ja_JP/docs/index.html
にあるサンプルに多少手を入れて動作を理解しようとしたものです。
package main
import (
"fmt"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
)
type Product struct {
gorm.Model
Code string
Price uint
Qty uint
}
/*
// gorm.Modelの定義
type Model struct {
ID uint `gorm:"primaryKey"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt gorm.DeletedAt `gorm:"index"`
}*/
var products []Product
func main() {
db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})
if err != nil {
panic("failed to connect database")
}
// Migrate the schema
db.AutoMigrate(&Product{}) // if you use Automigrate and change struct, it won't be reflected automatically
// Create
db.Create(&Product{Code: "D42", Price: 100, Qty: 20})
db.Create(&Product{Code: "A42", Price: 300, Qty: 5})
// Read
var product Product
//db.First(&product, 1) // find product with integer primary key
db.First(&product, "code = ?", "D42") // find product with code D42
fmt.Println(product.ID, product.Code, product.Price, product.Qty)
// Update
db.Model(&product).Update("Price", 200) // update only pointer(*) reocord
fmt.Println(product.ID, product.Code, product.Price, product.Qty)
db.Model(&product).Update("Qty", 30) // *:initial Create record pointer
fmt.Println(product.ID, product.Code, product.Price, product.Qty)
db.Model(&product).Updates(Product{Price: 200, Code: "F42"}) // non-zero fields
fmt.Println(product.ID, product.Code, product.Price, product.Qty)
db.Model(&product).Updates(map[string]interface{}{"Price": 500, "Code": "F42"})
fmt.Println(product.ID, product.Code, product.Price, product.Qty)
db.Model(&products).Where("ID = ?", 2).Update("Price", 1000) // search ID and update record
// Delete - delete product with condition
//db.Delete(&products, "Code = ?", "F42") // delete all of the code = "D42"
db.Find(&products) // SELECT * FROM Product;
for i, p := range products{
//db.Model(&p).Update("Qty", 20)
fmt.Println(i, p)
}
}
以下が実行結果になります。意外だったのは、db.Modelは現在のID(Primary key)のレコードに対して働くということ。
・実行一回目
1 D42 100 20
1 D42 200 20
1 D42 200 30
1 F42 200 30
1 F42 500 30
0 {{1 2023-04-01 09:25:58.10815 +0900 +0900 2023-04-01 09:25:58.110959 +0900 +0900 {0001-01-01 00:00:00 +0000 UTC false}} F42 500 30}
1 {{2 2023-04-01 09:25:58.10884 +0900 +0900 2023-04-01 09:25:58.111384 +0900 +0900 {0001-01-01 00:00:00 +0000 UTC false}} A42 1000 5}
・実行二回目
3 D42 100 20
3 D42 200 20
3 D42 200 30
3 F42 200 30
3 F42 500 30
0 {{1 2023-04-01 09:25:58.10815 +0900 +0900 2023-04-01 09:25:58.110959 +0900 +0900 {0001-01-01 00:00:00 +0000 UTC false}} F42 500 30}
1 {{2 2023-04-01 09:25:58.10884 +0900 +0900 2023-04-01 09:26:30.669874 +0900 +0900 {0001-01-01 00:00:00 +0000 UTC false}} A42 1000 5}
2 {{3 2023-04-01 09:26:30.666797 +0900 +0900 2023-04-01 09:26:30.669524 +0900 +0900 {0001-01-01 00:00:00 +0000 UTC false}} F42 500 30}
3 {{4 2023-04-01 09:26:30.667772 +0900 +0900 2023-04-01 09:26:30.667772 +0900 +0900 {0001-01-01 00:00:00 +0000 UTC false}} A42 300 5}
データベースは存在しなければgorm.Opendで
自動作成されます。
db.Createがレコードの新規作成、db.Modelがレコードの更新、使ってませんがdb.Deleteがレコード削除です。条件付けはwhere句を使うのでSQLと類似しているように思います。またdb.Firstはレコードを一個だけ(SELECT * FROM products ORDER BY id LIMIT 1;と等価)引っ張ってくるしde.Findはselect*from相当です。
gorm.Modelを構造体に記載するとプライマリーキーや時刻が自動的にレコードに追記されますが、いまいち余計な感じもします。またdb.AutoMigrateはテーブル作成後に構造体を変更しても反映されないようなのでこれも注意が必要でしょう。したがってサンプルのままに使用することはそれほどないように思います。
gorm.Modelを使わなければ、例えば以下のようにP-Keyをセットすれば良いでしょう。
type Product struct {
ID int `gorm:"primaryKey"`
Code string
Price uint
Qty uint
}
いずれにしてもSQL文を作成するよりは間違いが入り込みづらいように見えるので、できる限りORMを使うのが良さそうです。
P.S. 2023/4/2
- 1. テーブル名の指定
テーブル名ってどうなるかと思ったら、構造体の複数形(ドキュメントの説明:構造体 User
の場合、対応するテーブル名は規約により users
となります。)が使われるようです。テーブル名をダイナミックに設定したいと思ったらちょっと不便かもしれない。
sqlite> select*from products;
1|F42|500|30|2023-04-02 10:54:48.693791+09:00
2|A42|1000|5|2023-04-02 10:54:48.694968+09:00
3|F42|500|30|2023-04-02 14:13:44.198783+09:00
4|A42|300|5|2023-04-02 14:13:44.200078+09:00
テーブル名はTabler
インターフェイスの実装で変更できるようですね。
https://gorm.io/ja_JP/docs/conventions.html
以下を追加すると、
func (Product) TableName() string {
t := time.Now()
tableName := "tbl" + strconv.Itoa(t.Year()) + strconv.Itoa(t.YearDay())
return tableName
}
テーブル名の変更指定ができました。
sqlite> .tables
products tbl202392
2. SQL文を出力したい
通常はSQL文は隠蔽されますが、Debug()関数を挟んでやれば、SQL相当のステートメントを出力してくれます。
db.Debug().Find(&products) // SELECT * FROM Product;
for i, p := range products{
fmt.Println(i, p)
}
こんな感じで、
2023/04/02 20:21:46 ~~~/orm.go:71
[0.118ms] [rows:10] SELECT * FROM `tbl202392`
~途中省略
5 { ゲスト5 1680422277 0}
6 { ゲスト6 1680422277 0}
7 { ゲスト7 1680422277 0}
8 { ゲスト8 1680422277 0}
9 { ゲスト9 1680422277 0}
admin
“GoのORMであるGORMを見てみる” への1件の返信
コメントは受け付けていません。