LoginSignup
17
14

More than 5 years have passed since last update.

RevelでGORMを使ったMySQL Associationの実装

Last updated at Posted at 2017-01-02

GoのORMのGORMを使ってアソシエーションをしようとしたらはまったのでメモ。

RevelでMySQLに接続

gormやmysqlのインストールがまだだったら、下記コマンドで。

$ go get github.com/jinzhu/gorm
$ go get github.com/go-sql-driver/mysql

ModelにGORMの接続設定を書く。
DBの接続情報はconf/app.conf[dev]セクションに書く。

app/models/grom.go
package models

import (
  "github.com/revel/revel"
  "github.com/jinzhu/gorm"
  "strings"
  "time"
  "fmt"
  _"github.com/go-sql-driver/mysql"
)

var DB **gorm.DB

func InitDB() {
  db, err := gorm.Open("mysql", getConnectionString())

  if err != nil {
    revel.ERROR.Println("FATAL", err)
    panic(err)
  }

  db.DB()
  DB = &db
}

type Model struct {
  gorm.Model
  ID        uint `gorm:"primary_key"`
  CreatedAt time.Time
  UpdatedAt time.Time
  DeletedAt *time.Time
}

type Validator interface {
  IsSatisfied(interface{}) bool
  DefaultMessage() string
}

func getParamString(param string, defaultValue string) string {
  p, found := revel.Config.String(param)
  if !found {
    if defaultValue == "" {
      revel.ERROR.Fatal("Cound not find parameter: " + param)
    } else {
      return defaultValue
    }
  }
  return p
}

func getConnectionString() string {
  host := getParamString("db.host", "localhost")
  port := getParamString("db.port", "3306")
  user := getParamString("db.user", "username")
  pass := getParamString("db.password", "password")
  dbname := getParamString("db.name", "dbname")
  protocol := getParamString("db.protocol", "tcp")
  dbargs := getParamString("dbargs", " ")
  timezone := getParamString("db.timezone", "parseTime=true&loc=Asia%2FTokyo")

  if strings.Trim(dbargs, " ") != "" {
    dbargs = "?" + dbargs
  } else {
    dbargs = ""
  }
  return fmt.Sprintf("%s:%s@%s([%s]:%s)/%s%s?%s", user, pass, protocol, host, port, dbname, dbargs, timezone)
}

これをアプリケーション起動時に実行されるようにapp/init.goに追記する。

app/init.go
func init() {
  revel.OnAppStart(models.InitDB)
}

これでMySQLに接続する準備は整いました。

構造体の定義

user belongsTo teamteam hasMany usersで実装を進めます。

基本構造体の定義

汎用的なカラムは基本構造体として定義しちゃいます。

type Model struct {
  ID        uint `gorm:"primary_key"`
  CreatedAt time.Time
  UpdatedAt time.Time
  DeletedAt *time.Time
}

Userの構造体を定義

gorm.Modelは基本構造体を使うよ!という宣言になります。宣言しない場合は、IDから自分で書きます。
gorm:"ForeignKey:TeamID;AssociationForeignKey:ID"は外部キーなどが変わった名前の場合に使います。今回はなくても動きます。

type User struct {
  gorm.Model
  TeamID        int
  Name          string
  Email         string
  Password      []byte
  RememberToken string
  Team          Team `gorm:"ForeignKey:TeamID;AssociationForeignKey:ID"`
}

Teamの構造体を定義

type Team struct {
  gorm.Model
  Name  string
  Users []User
}

Query

本当はWhere()を使いたかったんだけど、Where()を使うとRelated()が発動しないみたい・・・。結果、「こ、これでいいのか・・・?」というコードに。

OKパターン

多対一の場合

func GetUserByEmail(email string) (user User) {
  (*DB).Limit(1).Find(&user, "email = ?", email).Related(&user.Team)
  return
}

IDで取り出す場合はFirst()でもOK

func GetUserById(id int) (user User) {
  (*DB).First(&user, id).Related(&user.Team)
  return
}

一対多の場合

取り出し方は多対一と同じです。

func GetTeamByName(teamName string) (team Team) {
  (*DB).Limit(1).Find(&team, "name = ?", teamName).Related(&team.Users)
  return
}

NGパターン

これだとUserは取れるんだけど、user.Teamが取れていない(なぜかはわかっていない)。

func GetUserByEmail(email string) (user User) {
  (*DB).Where("email = ?", email).First(&user).Related(&user.Team)
  return
}

参考

17
14
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17
14