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

執筆者:


  1. Mathias says:

    Anyway just wanted to say thanks for your website. Stuff like
    this allow me to stay on track and helps keep me. I am hoping that you
    continue to develop, along with can discover it! Good luck and thanks again.
    Anyway, I really like keto up to now, although I’m experimenting with trying to set my own spin on it.

    It’s important to be flexible with your diet, even if you’re locked into
    something like”keeping carbohydrates low”. I want to lose weight, however I don’t want to
    be the individual eating out that orders something odd off the menu, or even nothing
    at all. If your daily diet comes at the cost of your happiness,
    if you ask me it’s just flat out not worth it. xoxo That is GREAT.
    Been doing my very best to attempt to do quality research,
    so things like this really helps. Anyone else think that
    the biggest issue people have with weight loss comes from them not putting in the work ?

    You have to be inclined to do a bit of research first, although
    you want to begin losing weight ASAP like I do.
    I’m sorry to say you are just going to have issues if you do
    not do your part.

comment

Your email address will not be published.

関連記事

Go言語 GORM+GinでTODOリストのAPIを作ってみた

前回の「Go言語 GORM+GinでTODOリストを作ってみた」に続いて「GORM+Gin」でTODOリストのAPIを作ってみました。ソースコードは前回からの差分だけを記載しています。できたものは下記URLから確認できます。 http://sandbox.taisablog.com/api/v1/ GinのGithub 事前情報 Webフレームワーク:Gin (https://github.com/gin-gonic/gin) ORM:GORM (https://gorm.io/docs) DB:MySQL ルーティングは今回はAPIなので以下としました。モデルをtasksにすればよかったと思いましたが一旦このままにしておきます。 [GIN-debug] GET /todo –> main.main.func1 (3 handlers) // 一覧表示 [GIN-debug] POST /todo –> main.main.func2 (3 handlers) // 新規作成 [GIN-debug] GET /todo/:id –> main.main.func3 (3 handlers) // 編集画面表示 [GIN-debug] PUT /todo/:id –> main.main.func4 (3 handlers) // 編集 [GIN-debug] DELETE /todo/:id –> main.main.func5 (3 handlers) // 削除 ディレクトリ構成 . ├── api │   └── v1 │   └── todo.go ├── controllers │   └── todo.go ├── db │   └── db.go ├── main.go ├── models │   └── todo.go ├── router    └── router.go router.go router.goにr.Group(“/api/v1”)のAPI用のグループを追加してルーティングを追加しました。 package router import ( “github.com/gin-contrib/cors” “github.com/gin-gonic/gin” “github.com/jinzhu/gorm” v1 “github.com/taisa831/sandbox-gin/api/v1” “github.com/taisa831/sandbox-gin/controllers” …

Golangを使ってJWTを15分で理解する

JWTとは JWT(ジョットと言うらしい)はJSON Web Tokenの略で、JSONをベースとしたアクセストークンのためのオープン標準 (RFC 7519) です。色々記事を見ましたが、最終的にWikipediaが分かりやすく一番参考にしました。https://ja.wikipedia.org/wiki/JSON_Web_Token JWTの構造 JWTは以下の3つの要素をピリオドで区切った文字列で構成されます。 ヘッダー 署名生成に使用するアルゴリズムを格納します。下記のHS256は、このトークンがHMAC-SHA256で署名されていることを示しています。署名アルゴリズムとしては、SHA-256を使用したHMAC (HS256) や、SHA-256を使用したRSA署名 (RS256) がよく用いられます。 { “alg” : “HS256”, “typ” : “JWT” } ペイロード 認証情報などのクレームを格納します。クレームとはペイロードに含める以下のような標準フィールド(クレーム)を指します。JWTの仕様では、トークンに一般的に含まれる7つの標準フィールドが定義されています。また用途に応じた独自のカスタムフィールドを含むこともできます。下記の例では、トークン発行日時を示す標準のクレーム (iat) と、カスタムクレーム (loggedInAs) を格納しています。 { “loggedInAs” : “admin”, “iat” : 1422779638 } 7つのペイロードの標準クレーム 署名 トークン検証用の署名です。署名はヘッダーとペイロードをBase64urlエンコーディングしてピリオドで結合したものから生成します。署名はヘッダーで指定された暗号化アルゴリズムにより生成されます。下記はHMAC-SHA256形式でのコード例です。 HMAC-SHA256(base64urlEncoding(header) + ‘.’ + base64urlEncoding(payload), ‘secret key’) JWTを使用するにあたって JWTはトークンが返され、それをローカルに保存して利用します(主にlocal storageやsession storageが用いられますが、セッションIDのようにCookieを用いる場合もあります。) 認証時にはAuthorizationヘッダーでBearerスキーマを利用します。またサーバー上に認証状態を保持しないステートレスな認証方式です。その為JWT単体ではトークンを無効にすることが出来ません。サーバーに状態を保持すれば可能ですが、その場合ステートレスの利点は失われることになります。さて、ここまではほぼ Wikipedia に書いてある内容そのままです。ここから実際にGo/GinのJWT Middlewareを使って実際の動作を確認してみます。 Go/GinのJWT Middlewareを使った動作確認 利用するJWT Middlewareについて ここでは、「https://github.com/gin-gonic/gin」 を使う前提で、次のMiddlewareを利用します。「https://github.com/appleboy/gin-jwt」。このMiddlewareは、auth_jwt.goの1ファイルでで構成されていて、「https://github.com/dgrijalva/jwt-go」 をGin用に薄くラップしたものです。jwt-goはトークンを作成したりパースしたり様々な機能が用意されています。 サンプルソース サンプルソースは、https://github.com/appleboy/gin-jwt/blob/master/README.md に載っているのでこれを元に確認します。処理は大きく「ログイン時にToken発行する」と「トークン認証&処理実行する」の2種類あります。 ログイン時にToken発行する ログイン時にTokenを発行する処理は、LoginHandlerです。Routerでは次のように定義しています。LoginHandlerではAuthenticatorとPayloadFuncが呼ばれる為、Middlewareにてこれらを実装する必要があります。 r.POST(“/login”, authMiddleware.LoginHandler) Authenticatorはログイン認証の為の関数です。例では固定値が設定されていますが、実際は主にDBから値を取得することになると思います。PayloadFuncはペイロードに含めるクレームを設定します。ペイロードには任意のクレームを追加可能なので、ログインIDとなるuserIDをセットしています。 // ログインに基づいたユーザの認証振る舞いをするコールバック Authenticator: func(c *gin.Context) (interface{}, error) { var loginVals login if err := c.ShouldBind(&loginVals); err != nil { return “”, jwt.ErrMissingLoginValues } userID := loginVals.Username password := loginVals.Password // …

Go言語 ORMライブラリ GORMの使い方

Go言語 ORMライブラリのGORMの簡単な使い方を確認してみました。また、公式ドキュメントにしっかりと使い方が書いてありますので基本的にはそちらを参考にしてもらえればと思います(すべてではないですが日本語訳もされています)。その上でクイックスタートを元に簡単な使い方と挙動を確認してみます。 http://gorm.io/ja_JP/docs/ インストール 以下のコマンドでインストールできます。 go get -u github.com/jinzhu/gorm クイックスタート 公式ドキュメントにあるクイックスタートを実行してみました。DBだけsqliteではなくmysqlに変更しています。 package main import ( “github.com/jinzhu/gorm” // _ “github.com/jinzhu/gorm/dialects/sqlite” _ “github.com/jinzhu/gorm/dialects/mysql” ) type Product struct { gorm.Model Code string Price uint } func main() { // db, err := gorm.Open(“sqlite3”, “test.db”) db, err := gorm.Open(“mysql”, “gorm:gorm@/sandbox?charset=utf8mb4&parseTime=True&loc=Local”) if err != nil { panic(“データベースへの接続に失敗しました”) } defer db.Close() // スキーマのマイグレーション db.AutoMigrate(&Product{}) // Create db.Create(&Product{Code: “L1212”, Price: 1000}) // Read var product Product db.First(&product, 1) // idが1の製品を探します db.First(&product, “code = ?”, “L1212”) // codeがL1212の製品を探します // Update – 製品価格を2,000に更新します db.Model(&product).Update(“Price”, 2000) // Delete – 製品を削除します db.Delete(&product) } 実行してみるとproductsテーブルが作成され、以下のカラムとレコードができました。structでは宣言していない、id、created_at、updated_at、deleted_atカラムができ、deleted_atに日付が入りソフトデリートが行われています。 go run main.go gorm.Model gorm.Modelを宣言するとid、created_at、updated_at、deleted_atカラムが自動的に注入されます。また,deleted_atカラムがある場合、Deleteはソフトデリートになります。 参考: http://gorm.io/ja_JP/docs/conventions.html …

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

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’ …