🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
Echo(フレームワーク)

Echoは、Go言語で作られたフレームワーク。非常に軽量で、小~中規模のアプリ構成を想定した仕様になっています。公式ドキュメントが用意されており、初心者でも始めやすい点が特徴です。

Go

Go(golang)は、Googleで開発されたオープンソースのプログラミング言語です。

Q&A

解決済

1回答

3959閲覧

[Go] echo, GORMを使ったREST API実装についての質問

tailer

総合スコア62

Echo(フレームワーク)

Echoは、Go言語で作られたフレームワーク。非常に軽量で、小~中規模のアプリ構成を想定した仕様になっています。公式ドキュメントが用意されており、初心者でも始めやすい点が特徴です。

Go

Go(golang)は、Googleで開発されたオープンソースのプログラミング言語です。

0グッド

2クリップ

投稿2019/09/16 05:50

編集2019/09/16 06:10

Go初心者です。
勉強のために Echo GORMを使ったAPIを作っています。

ただいま usersテーブル に入っているレコードをパラメータで送られたきた id から一つ特定して取得する処理を書いています。 Railsでいう users_contoroller の showアクションのような処理を作りたいです。
しかし、1回目のGETでは成功するクエリが2回目以降失敗してしまい困っています。(下記で説明)
どなたか解決策をご教授頂けますと幸いですm(_ _)m

ディレクトリ構造は以下のようになります

. |-- controllers | `-- users.go |-- db | `-- connect.go |-- main.go `-- models `-- user.go

各ファイルのは以下のようになっています。

↓ main.go

package main import ( "github.com/labstack/echo" "github.com/labstack/echo/middleware" "echo_first/controllers" ) func main() { e := echo.New() e.Use(middleware.Logger()) e.Use(middleware.Recover()) // ルーティング e.GET("user/:id", users_controller.Show()) // サーバー起動 e.Logger.Fatal(e.Start(":3000")) }

↓ models/user.go

package model type User struct { Id int Name string Birth_day int }

↓ db/connect.go

package dbconnect import ( "github.com/jinzhu/gorm" _ "github.com/jinzhu/gorm/dialects/mysql" "fmt" ) func Connect() *gorm.DB{ db, err := gorm.Open("mysql", "hogeuser:@/hogehoge?charset=utf8&parseTime=True&loc=Local") if err != nil { fmt.Print("データベース接続に失敗しました。") } db.LogMode(true) return db }

↓ conrollers/user.go

package users_controller import ( "github.com/labstack/echo" "net/http" "fmt" "echo_first/models" "echo_first/db" ) func Show() echo.HandlerFunc{ db := dbconnect.Connect() user := new(model.User) return func(c echo.Context) error{ user_id := c.Param("id") result := db.First(&user, "id = ?", user_id) if result.RecordNotFound() { fmt.Println("レコードが見つかりません") } return c.JSON(http.StatusOK, result) } }

サーバーを起動立ち上げ1回目のGETリクエスト user/1では以下のようにデータを取得できます。

[25.45ms] SELECT * FROM `users` WHERE (id = '1') ORDER BY `users`.`id` ASC LIMIT 1 [1 rows affected or returned ]

しかし2回目のGETリクエスト user/2ではSQLがおかしく、データを取得できません。

[29.71ms] SELECT * FROM `users` WHERE `users`.`id` = 1 AND ((id = '2')) ORDER BY `users`.`id` ASC LIMIT 1 [0 rows affected or returned ] レコードが見つかりません

データベースのコネクションの問題なのか、GORMのクエリの問題なのか分からず躓いてしまいました。
データベースのコネクションの問題の場合はどこで db.Closeするのか分かりません...。リクエストの度にデータベースに接続して Closeする??のでしょうか。。
どなたかご教授お願い致します( ; ; )

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2019/09/16 06:07

gormだったらgorm.Open()とすると思うんですがdbconnectとは何ですか? echo特有のwrapperか何かでしょうか。
退会済みユーザー

退会済みユーザー

2019/09/16 06:09

> REAR API とは何ですか?
退会済みユーザー

退会済みユーザー

2019/09/16 06:12

> gormだったらgorm.Open()とすると思うんですがdbconnectとは何ですか? db/connect.goを見て理解しました。 失礼しました。
tailer

2019/09/16 06:12

dbへの接続をファイル分割したかったので dbconnectパッケージを作って, その中の Connect関数でgorm.Open()を使っています。 > REAR API とは何ですか? こちらはtypoでした。ご指摘ありがとうございます
guest

回答1

0

ベストアンサー

とりあえずCloseは必要だと思います。

diff

1func Show() echo.HandlerFunc{ 2 db := dbconnect.Connect() 3+ defer db.Close() 4 user := new(model.User) 5 return func(c echo.Context) error{ 6 user_id := c.Param("id") 7 result := db.First(&user, "id = ?", user_id) 8 if result.RecordNotFound() { 9 fmt.Println("レコードが見つかりません") 10 } 11 return c.JSON(http.StatusOK, result) 12 } 13}

go

1func Show() echo.HandlerFunc{ 2 return func(c echo.Context) error { 3 db := dbconnect.Connect() 4 defer db.Close() 5 user := new(model.User) 6 user_id := c.Param("id") 7 result := db.First(&user, "id = ?", user_id) 8 if result.RecordNotFound() { 9 fmt.Println("レコードが見つかりません") 10 } 11 return c.JSON(http.StatusOK, result) 12 } 13}

ではこうしてはどうでしょう。

投稿2019/09/16 06:15

編集2019/09/16 06:32
退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

tailer

2019/09/16 06:26

ご回答ありがとうございます。 `defer db.Close()`をご指摘の場所に入れた場合はデータが取得できず想定通りの動作はしません... 以下がログになります。 ``` ____ __ / __/___/ / ___ / _// __/ _ / _ \ /___/\__/_//_/\___/ v4.1.10 High performance, minimalist Go web framework https://echo.labstack.com ____________________________________O/_______ O\ ⇨ http server started on [::]:3000 [2019-09-16 15:16:41] sql: database is closed [2019-09-16 15:16:41] [0.13ms] SELECT * FROM `users` WHERE (id = '2') ORDER BY `users`.`id` ASC LIMIT 1 [0 rows affected or returned ] ``` deferが設定された関数の実行タイミングは returnの直前となっているため、`db.First(&user, "id = ?", user_id)`が実行される前にdb.Closeされているのが原因かと思います。 どこかでdb.Closeするのかが難しいですね...( ; ; )
tailer

2019/09/16 06:37

回答ありがとうございます。 こちらで期待通りのSQLが発行され、上手く動作できました!本当に助かりましたm(_ _)m 厚かましいですが最後に一つ質問させてください。 db.Openとdb.Closeはhttpリクエストの度に行うという認識で良いのでしょうか?
退会済みユーザー

退会済みユーザー

2019/09/16 06:52

場合によります。 リクエスト毎にコネクションを張って切るのが最もシンプルな形であるとはいえると思います。 よりパフォーマンスを気にするということであればコネクションプールについて調べれば良いと思います。
退会済みユーザー

退会済みユーザー

2019/09/16 06:53

後者の場合でもdb.Close()は必ず必要なのでそこは間違えないようにしてください。
tailer

2019/09/16 08:25

勉強になりました。ありがとうございました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問