taisablog

taisa's engineer blog

Go

GORMでよく使うSQLの書き方

投稿日:


GORMでよく使うSQLの書き方をメモとして残しておきます。詳細は記事最後の参考リンクの公式ドキュメントで確認できます。

CREATE

insertするだけであれば create を利用します。

db.Create(&models.User{Name: "user"})
# 実行SQL
INSERT INTO "users" ("created_at","updated_at","name","email") VALUES ('2020-04-25 11:22:00','2020-04-25 11:22:00','user',0)

UPDATE

特定のフィールドだけ更新したい場合、 Update と Updates を使います。

update

一つのフィールドだけアップデートします。

user := &models.User{Name: "user"}
db.Create(user)
db.Model(user).Update("name", "user2")
# 実行SQL
INSERT INTO "users" ("created_at","updated_at","name","email") VALUES ('2020-04-25 11:34:39','2020-04-25 11:34:39','user','')

UPDATE "users" SET "name" = 'user2', "updated_at" = '2020-04-25 11:34:39'  WHERE "users"."id" = 5

updates

updates は map を利用して複数のフィールドをアップデートします。

user := &models.User{Name: "user"}
db.Create(user)
db.Model(user).Updates(map[string]interface{}{"name": "user3", "email": "g5.taisa831@gmail.com"})
# 実行SQL
INSERT INTO "users" ("created_at","updated_at","name","email") VALUES ('2020-04-25 11:39:52','2020-04-25 11:39:52','user','')

UPDATE "users" SET "email" = 'g5.taisa831@gmail.com', "name" = 'user3', "updated_at" = '2020-04-25 11:39:52'  WHERE "users"."id" = 6

SAVE

Save は SQL を実行する際にすべてのフィールドを含みます。フィールドを指定しなくても空にはなりません。

user := &models.User{Name: "user", Email: "g5.taisa831@gmail.com"}
db.Create(user)

user.Name = "user2"
db.Save(user)
# 実行SQL
INSERT INTO "users" ("created_at","updated_at","name","email") VALUES ('2020-04-25 11:43:30','2020-04-25 11:43:30','user','g5.taisa831@gmail.com')

UPDATE "users" SET "created_at" = '2020-04-25 11:43:30', "updated_at" = '2020-04-25 11:43:30', "name" = 'user2', "email" = 'g5.taisa831@gmail.com'  WHERE "users"."id" = 8 

Assign FirstOrCreate

create or update を利用するには AssignFirstOrCreate を利用します。

レコードが存在しない場合

user := &models.User{}
db.Where("name = ?", "non user").Assign(models.User{Name: "user"}).FirstOrCreate(&user)
# 実行SQL
SELECT * FROM "users"  WHERE (name = 'non user') ORDER BY "users"."id" ASC LIMIT 1

INSERT INTO "users" ("created_at","updated_at","name","email") VALUES ('2020-04-25 12:04:24','2020-04-25 12:04:24','user','') 

レコードが存在する場合

user := &models.User{}
db.Where("name = ?", "user").Assign(models.User{Name: "user2"}).FirstOrCreate(user)
# 実行SQL
SELECT * FROM "users"  WHERE (name = 'user') ORDER BY "users"."id" ASC LIMIT 1

UPDATE "users" SET "name" = 'user2', "updated_at" = '2020-04-25 12:05:45'  WHERE "users"."id" = 3 AND ((name = 'user'))

READ

1レコードだけ取得するにはFirstを利用します。

user := &models.User{Name: "user", Email: "g5.taisa831@gmail.com"}
db.Create(user)

db.First(user, "name = ?", "user")
db.Where("name = ?", "user").First(user)
# 実行SQL
INSERT INTO "users" ("created_at","updated_at","name","email") VALUES ('2020-04-25 11:48:25','2020-04-25 11:48:25','user','g5.taisa831@gmail.com') 

SELECT * FROM "users"  WHERE "users"."id" = 10 AND ((name = 'user')) ORDER BY "users"."id" ASC LIMIT 1

SELECT * FROM "users"  WHERE "users"."id" = 10 AND ((name = 'user')) ORDER BY "users"."id" ASC LIMIT 1 

JOIN

joinのサンプルは下記となります。

user := &models.User{Name: "user", Email: "g5.taisa831@gmail.com"}
db.Create(user)

post := &models.Post{
		Post:      "post",
		UserId:    user.ID,
	}
db.Create(post)

var users []models.User
db.Table("users").Select("users.*, posts.*").Joins("inner join posts on users.id = posts.user_id").Find(&users)
# 実行SQL
INSERT INTO "users" ("created_at","updated_at","name","email") VALUES ('2020-04-25 12:26:39','2020-04-25 12:26:39','user','g5.taisa831@gmail.com')

INSERT INTO "posts" ("created_at","updated_at","user_id","post") VALUES ('2020-04-25 12:26:39','2020-04-25 12:26:39',14,'post')

SELECT users.*, posts.* FROM "users" inner join posts on users.id = posts.user_id 

> user

PRELOAD

UserモデルにPostsを追加すると PRELOAD が利用できます。PRELOAD を利用すると JOIN せずに関連レコードを取得することができます。下記のようなJSONを返したいときに便利です。

users {
	"id": "1",
        "name": "user",
	"posts" [
		{},
		{},
		{},
		{},
	]
}
type User struct {
	ID        uint `gorm:"primary_key"`
	CreatedAt time.Time
	UpdatedAt time.Time
	Posts     Post
	Name      string
	Email     string
}

type Post struct {
	ID        uint `gorm:"primary_key"`
	CreatedAt time.Time
	UpdatedAt time.Time
	UserId    uint
	Post      string
}

user := &models.User{Name: "user", Email: "g5.taisa831@gmail.com"}
db.Preload("Posts").Find(&user)
# 実行SQL
SELECT * FROM "users"  WHERE "users"."id" = 16 

SELECT * FROM "posts"  WHERE ("user_id" IN (16))

参考

-Go

執筆者:

関連記事

[Golang]Goのio/ioutilパッケージは分かりやすくて使いやすい

Goのioパッケージは主にインターフェースになっていて他のパッケージで多く実装されています。またioパッケージにもパブリックな関数がありファイルの入出力はできますが少し細かい処理になります。io/ioutilパッケージを使うとファイルの入出力処理が簡単にできます。以下にio/ioutilパッケージを使った処理とそれに対するテストコードを記載します。 io/ioutil/ioutil.go ReadAll() func ReadAll() string { file, _ := os.Open(“testdata/src.txt”) b, _ := ioutil.ReadAll(file) return string(b) } ReadAllテスト func TestReadAll(t *testing.T) { str := ReadAll() if str != “0123456789” { t.Errorf(“TestReadAll Error. %s”, str) } } ReadFile() func ReadFile() string { b, _ := ioutil.ReadFile(“testdata/src.txt”) return string(b) } ReadFileテスト func TestReadFile(t *testing.T) { str := ReadFile() if str != “0123456789” { t.Errorf(“TestReadAll Error. %s”, str) } } WriteFile() func WriteFile() string { b := []byte(“0123456789”) _ = ioutil.WriteFile(“testdata/dst.txt”, b, os.ModePerm) b, _ = ioutil.ReadFile(“testdata/dst.txt”) return string(b) } WriteFileテスト func TestWriteFile(t *testing.T) { str := WriteFile() if str != “0123456789” …

[Golang] Goを始めたらまずはioパッケージを知るべし

Goを書いているとio.writerとio.readerを扱うケースが頻繁に出てきますが、これはioパッケージが多くの他のパッケージのインターフェースになっているからなのでioパッケージを知っておくことで開発が楽になります。 参考書籍 本書はGoのio.Writer、io.Readerからはじまりシステムの深いところまで丁寧に説明されているのでとてもおすすめです。ioパッケージから始まっているのも納得です。Webで無料で見れますし書籍版、PDF版で購入も可能です。 Web版 https://ascii.jp/elem/000/001/235/1235262/ 書籍版 Goならわかるシステムプログラミング ioパッケージのインターフェース一覧 ioパッケージのインターフェース一覧です。Goのインターフェースの実装は、明示的にインターフェースを明示的にimplementsせず、インターフェースを満たしていたらimplementsしていることになります。 Reader(インターフェース)Writer(インターフェース)Seeker(インターフェース)Closer(インターフェース)ReadWriter(複合インターフェース)ReadCloser(複合インターフェース)WriteCloser(複合インターフェース)ReadSeeker(複合インターフェース)WriteSeeker(複合インターフェース)ReadWriteCloser(複合インターフェース)ReadWriteSeeker(複合インターフェース)ReaderFrom(インターフェース)WriterTo(インターフェース)ReaderAt(インターフェース)WriterAt(インターフェース)ByteReader(インターフェース)ByteScanner(インターフェース)ByteWriter(インターフェース)RuneScanner(インターフェース)StringWriter(インターフェース) 複合インターフェース表 Goではインターフェースにインターフェースを食わせることができ、ioパッケージで作られている複合インターフェースは以下となります。 インターフェースio.Readerio.Writerio.Seekerio.Closerio.ReadWriter◯◯  io.ReadSeeker◯ ◯ io.ReadCloser◯  ◯io.WriteSeeker ◯◯ io.WriteCloser ◯ ◯io.ReadWriteSeeker◯◯◯ io.ReadWriteCloser◯◯ ◯ インターフェースを満たしている一覧を確認する方法 以下のコマンドを叩くと対象のインターフェースを満たしているものの一覧が確認できます。 $ GOPATH=/ godoc -http “:6060” -analysis type ## 実行後以下のURLにアクセスすると`io`パッケージが確認できる http://localhost:6060/pkg/io/ Readerのimplements一覧 テストも含んでいますが沢山あります pointer type *archive/tar.Reader implements Readerpointer type *archive/tar.regFileReader implements Readerpointer type *archive/tar.sparseFileReader implements Readerpointer type *archive/tar.testFile implements Readerpointer type *archive/zip.checksumReader implements Readerpointer type *archive/zip.pooledFlateReader implements Readerpointer type *bufio.Reader implements Readerpointer type *bufio_test.StringReader implements Readerpointer type *bufio_test.emptyThenNonEmptyReader implements Readerpointer type *bufio_test.errorThenGoodReader implements Readerpointer type *bufio_test.negativeReader implements Readerpointer type *bufio_test.rot13Reader implements Readerpointer type *bufio_test.scriptedReader implements Readerpointer type *bufio_test.slowReader implements Readerpointer type *bufio_test.testReader implements Readerpointer type *bytes.Buffer implements Readerpointer type *bytes.Reader implements Readerpointer type *bytes_test.negativeReader implements Readerpointer …

Golang 1.13 released! The difference from 1.12 to 1.13

Golang 1.13 was released in 3 September 2019. This post has difference from 1.12 to 1.13. I check the changes at the release notes and difference of src. Release Notes Go 1.13 Release Notes The latest Go release, version 1.13, arrives six months after Go 1.12. Most of its changes are in the implementation of the toolchain, runtime, and libraries. As always, the release maintains the Go 1 promise of compatibility. We expect almost all Go programs to continue to compile and run as before. Diff on GitHub Release branch.go1.13 by taisa831 · Pull Request #1 · taisa831/go This PR …

no image

【3分で作れる】Goのコマンドラインツール by Cobra

同僚のおすすめでコマンドラインツール作成にCobraを使いました。ものすごく簡単につくれるのですが、それでも少しハマったところがあったので、コマンドラインツールを作るまでの流れを書いておきます。 前提 go version go1.14.2 darwin/amd64利用ライブラリ:https://github.com/spf13/cobra本記事のサンプルコード:https://github.com/taisa831/sandbox-cobra Index 雛形を作成するコマンドを追加するコンフィグを追加するサブコマンドを追加するまとめ 雛形を作成する 雛形作成にはgeneratorを使います。go getしてcobraコマンドを利用可能にします。 go get -u github.com/spf13/cobra/cobra cobraコマンドを実行すると以下のようなusageが表示されます。 $ cobra Cobra is a CLI library for Go that empowers applications. This application is a tool to generate the needed files to quickly create a Cobra application. Usage: cobra [command] Available Commands: add Add a command to a Cobra Application help Help about any command init Initialize a Cobra Application Flags: -a, –author string author name for copyright attribution (default “YOUR NAME”) –config string config file (default is $HOME/.cobra.yaml) -h, –help help for cobra -l, –license string name of license for the project …

no image

docker-composeでGoのWebサーバーを起動する

ミニマムにやっておかないと忘れがちなのでメモ サンプル用Goサーバを作成する package main import ( “encoding/json” “net/http” ) type User struct { FirstName string `json:”firstName”` LastName string `json:”lastName”` } func users(w http.ResponseWriter, req *http.Request) { w.Header().Set(“Content-Type”, “application/json”) user := User{ FirstName: “John”, LastName: “Doe”, } var users []User users = append(users, user) json.NewEncoder(w).Encode(users) } func main() { http.HandleFunc(“/users”, users) http.ListenAndServe(“:8002”, nil) } 普通に起動して動作確認をします。 $ go run main.go http://localhost:8002/usersにアクセスするとJSON結果が出力されます。 [ { “firstName”: “John”, “lastName”: “Doe” } ] Dockerfile FROM golang:1.14 #FROM golang:1.14-alpine # コンテナログイン時のディレクトリ指定 WORKDIR /opt/sandbox-docker-compose-go # ホストのファイルをコンテナの作業ディレクトリにコピー COPY . . # ADD . . # ビルド RUN go build -o app main.go # 起動 CMD [“/opt/sandbox-docker-compose-go/app”] docker-compose.ymlファイル version: ‘3’ …