昨日から色々あってGo langで開発をはじめました。
超にわかですが、気づいたことやハマったことなどあれば備忘録を残しておこうと思います。
今回は、gormを使ってBelongToの関係にあるFishモデルとWaterAreeモデルのリレーションを組んでみます。
また、Fishは1匹ではなく、複数匹Selectする必要があります。
(1)Relationメソッドを使う方法
type Fish struct { ID uint `json:"id" gorm:"column:id"` Name string `json:"name" gorm:"column:name"` WaterAreaID uint `json:"-" gorm:"column:waterarea_id"` WaterArea WaterArea `json:"water_area"` } type WaterArea struct { ID uint `json:"id" gorm:"primary_key"` Name string `json:"name" gorm:"column:name"` }
var fishes []models.Fish db.Find(&fishes) for i := range fishes { db.Model(fishes[i]).Related(&fishes[i].WaterArea, "WaterArea") }
多分ドキュメント読んでたらこの方法でリレーションを組むのが先に思いつくんじゃないでしょうか?(知らんけど)
2567レコード分のFIshを返すのにかかったレスポンスタイムは582.688351msでした。ふええ・・・遅すぎです・・><
(DBはlocalhostに立ってますので、外部ネットワーク通信によるレイテンシはありません)
これ、典型的なn+1問題です。
Go書いてると、Mapなんかもforでまわして結合したりするので、違和感なく上記のようなコードを書いてしまいがちな気がします?
あと、今回の問題とは関係ないですがRelatedの第二引数に”WaterArea”を渡さないと(invalid association )で怒られました。
ex) (invalid association )で怒られないために
WaterAreaID→WaterAreaIdにしたら大丈夫です。
ただ、Lintの警告が出でて味が悪いです。
type Fish struct { ID uint `json:"id" gorm:"column:id"` Name string `json:"name" gorm:"column:name"` WaterAreaId uint `json:"-" gorm:"column:waterarea_id"` WaterArea WaterArea `json:"water_area"` } type WaterArea struct { ID uint `json:"id" gorm:"primary_key"` Name string `json:"name" gorm:"column:name"` }
参考 gormでbelongs to にハマった話 · polidog lab++
(2)Preloadメソッドを使う方法
モデルの定義は先と同じです。
forで走査するのではなく、Preloadメソッドを使うようにしてみます。
db.Preload("WaterArea").Find(&fishes)
2567レコード分のFIshを返すのにかかったレスポンスタイムは30.843664msでした。 発行されるクエリが2クエリになり、かなりレスポンスが改善されました。
その他詳しい使い方は公式ドキュメントをご参照ください。