🐷

GORMでテーブル名やカラム名指定するのに苦戦した話

2022/11/22に公開

ORMには不慣れで、SQLはそこそこ分かる。goを初めて触ってみたという私がgoでPostgreSQLの処理にGORMを使ってみました。
その時につまったところがあるので書いてみます。

何につまったか

結構色々遠回りしたんですが、結論から先に言ってしまうと
タイトルにもしている通りテーブル名やカラムの指定がうまくできていなくて、期待する結果が得られずにいました。

環境

  • go 1.17
  • gorm.io/driver/postgres v1.4.5
  • gorm.io/gorm v1.24.1

テーブル

サンプルとしてmenulistsというテーブルを用意しました。

モデル

DBのデータをやり取りしたり使ったりするのに必要な構造体です。

type Menulist struct {
	Id      int
	Menu    string
	Price   int
	Nondisplay bool
}

CRUD

GORMの詳細はドキュメントが充実しているのでこちらをご覧ください。
https://gorm.io/ja_JP/docs/

DBと接続

// dbUserなどの設定値は省略してます
var dbURI string
dbURI = fmt.Sprintf("postgres://%s:%s@%s/%s", dbUser, dbPwd, dbHost, dbName)
  
var err error
db, err = gorm.Open(postgres.Open(dbURI), &gorm.Config{
	Logger: dbLogger,
})

if err != nil {
	db = nil
	log.Print("Failed connect")
}

INSERT

menu := model.Menulist{
	Id:8,
	Menu:"タルト",
	Price:450,
	Nondisplay:false,
}
db.Create(&menu)
// 発行されるSQL INSERT INTO "menulists" ("menu","price","nondisplay","id") VALUES ('タルト',450,false,8) RETURNING "id"

SELECT

Menus := []model.Menulist{}
db.Order("id").Find(&Menus)
// 発行されるSQL SELECT * FROM "menulists" ORDER BY id

Order()はORDER BYがつきます。
ソートが不要ならなくていいです。

UPDATE

db.Model(&model.Menulist{}).Where("id = 3").Update("Nondisplay", true)
// 発行されるSQL UPDATE "menulists" SET "nondisplay"=true WHERE id = 3

DELETE

db.Debug().Where("id = 8").Delete(&model.Menulist{})
// 発行されるSQL DELETE FROM "menulists" WHERE id = 8

実際に発行されるSQLを確認するにはDebug()を追加すればOKです。
これを知るまで、なぜエラーになるのかわからず苦戦しました。

db.Debug().Order("id").Find(&Menus)

使ってみて

私の書き方だと、SELECT以外目に見えてコード量が減ってないのでコード量を減らすという恩恵は得られていません。
この辺りは工夫でさらに減らすことができるかもしれないと思います。
ORMを利用する目的は色々あると思うので、チーム内で必要かどうか検討して採用するといいのかなと思いました。私が参加しているチームでは必要性がありGORMが採用されています。

ちなみにGORMではExecを使うとSQLを利用することもできます。

db.Exec("DELETE FROM menulists WHERE id = 8")

つまったポイント

今回書きたかったのはここですが、最初に書いた通りテーブル名やカラム名の指定です。
ここまで書いてきた内容ではテーブル名やカラム名は定義しておいたMenulistによって設定されています。

はじめこの仕組みを知らずに、なぜこの書き方でGORMは解釈できるのかと思っていました。
改めて仕組みですが、GORMは構造体を作成するとその構造体とテーブルとを紐付けてくれます。

テーブル名:構造体の型名の複数形(Menulistだとmenulistsになる)
カラム名:構造体のメンバー名

そして、型名は途中が大文字だとスネークケースになり、
メンバー名は途中が大文字だと先頭がキャメルケースになります。
公式のドキュメントではカラム名もスネークケースになるはずですが、
検証ではなぜか先頭キャメルケースになりました。

https://gorm.io/ja_JP/docs/conventions.html

構造体のMenulistとNondisplayの途中を大文字にして検証

type MenuList struct {
	Id      int
	Menu    string
	Price   int
	NonDisplay bool
}

この状態でUPDATEした際生成されるSQL

UPDATE "menu_lists" SET "Nondisplay"=true WHERE id = 3

テーブル名もカラム名も違うのでエラーになります。

ちなみにTable()を使って直接テーブル名を指定することもできますが、冗長になりますし上記の知識を抑えて構造体を利用する方が良いのではないかと私は思います。

goの知識も不足していて構造体やそのメンバーを小文字にすると参照できなくなるというところや、テーブル名もごちゃごちゃいじってしまったため色々と絡まって、私は混乱しました。
理解できた今となってはなんてことない話なんですが、知らないとしんどい時間を過ごすので今回記事にしてみました。

もしどなたかのお役に立てば幸いです。

レスキューナウテックブログ

Discussion