前回は、Go言語にてMySQLのエラーハンドリングを実装しました。
今回は、MySQLの更新処理を実装していきます。
前回
プロジェクト構成
現在のプロジェクト構成は以下のようになっています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
api ├── README.md ├── app │ ├── application.go │ └── url_mappings.go ├── controllers │ ├── ping_controller.go │ └── products │ ├── products_controller.go │ └── products_controller_test.go ├── datasources │ └── mysql │ └── products_db │ └── products_db.go ├── domain │ └── products │ ├── product_dao.go │ ├── product_dao_test.go │ ├── product_dto.go │ └── product_test.go ├── main.go ├── services │ └── products_service.go └── utils ├── errors │ └── api_errors.go └── mysqlutils └── mysql_utils.go |
MySQLデータの一括更新
現時点では、商品の保存と取得しかできません。PUTメソッドを使って、既存の商品データをアップデートできるようにします。
URLマッピングの追加
最初にユーザーリクエストをマッピングし、指定の処理に振り分ける実装を行います。
1 2 3 4 5 6 7 8 9 10 11 12 |
// app/url_mappings.go package app import "github.com/gouser/money-boy/api/controllers/products" func mapUrls() { router.GET("/products/:product_id", products.GetProduct) router.POST("/products", products.CreateProduct) # new router.PUT("/products/:product_id", products.UpdateProduct) } |
router.PUTを追加しました。PUTメソッドがリクエストされた場合、UpdateProduct関数に処理が振り分けられます。
尚、UpdateProduct関数は、Controllerに設定します。
コントローラーの追加
コントローラーでは、URLマッピングから受け取ったリクエストを適切なサービスに振り分ける役割を担います。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
// controller/products/products_controller.go package products import ( "net/http" "strconv" "github.com/gin-gonic/gin" "github.com/gouser/money-boy/api/domain/products" "github.com/gouser/money-boy/api/services" "github.com/gouser/money-boy/api/utils/errors" ) // UpdateProduct - Update product func UpdateProduct(c *gin.Context) { productID, productErr := strconv.ParseUint(c.Param("product_id"), 10, 64) if productErr != nil { err := errors.NewBadRequestError("product id should be a number") c.JSON(err.Status, err) return } var product products.Product if err := c.ShouldBindJSON(&product); err != nil { apiErr := errors.NewBadRequestError("invalid json body") c.JSON(apiErr.Status, apiErr) return } product.ID = uint(productID) result, err := services.UpdateProduct(product) if err != nil { c.JSON(err.Status, err) return } c.JSON(http.StatusOK, result) } ... |
URLマッピングで、
を指定しており、ginのコンテキスト(c)でproduct idを取得することが可能です。"/products/:product_id"
その場合、URLを「http://localhost:8080/products/1」にする必要があります。
その後、指定したIDを保持したProduct Structを生成し、UpdateProductメソッドに処理を依頼してます。
UpdateProductは、サービスに実装します。
サービスの追加
次にサービスを追加します。
サービスには、ビジネスロジックを実装します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
// products_service.go package services import ( "github.com/jinzhu/gorm" "github.com/gouser/money-boy/api/domain/products" "github.com/gouser/money-boy/api/utils/errors" ) // UpdateProduct - Service func UpdateProduct(product products.Product) (*products.Product, *errors.ApiErr) { current, err := GetProduct(product.ID) if err = current.Get(); err != nil { return nil, err } // Change Product Info current.Name = product.Name current.Detail = product.Detail current.Price = product.Price current.Img = product.Img if err := current.Update(); err != nil { return nil, err } return current, nil } // GetProduct - Service func GetProduct(productID uint) (*products.Product, *errors.ApiErr) { p := &products.Product{Model: gorm.Model{ID: productID}} if err := p.Get(); err != nil { return nil, err } return p, nil } ... |
最初にGetサービスから既存の商品情報を取得します。
次に、Updateメソッド(これから実装する)にて情報の更新を行います。
Update処理の追加
公式サイトを参考に一括更新処理を追加してみましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
// domain/products/product_dao.go package products import ( "github.com/gouser/money-boy/api/datasources/mysql/products_db" "github.com/gouser/money-boy/api/utils/errors" "github.com/gouser/money-boy/api/utils/mysqlutils" ) // Update - product func (p *Product) Update() *errors.ApiErr { if result := products_db.Client.Save(&p); result.Error != nil { return mysqlutils.ParseError(result.Error) } return nil } |
Saveメソッドを使って、全フィールドの一括更新を行いました。
動作確認
動作確認をしてみましょう。
まず、「http://localhost:8080/products/1」にGETリクエストを送り、データの確認をします。

取得できたデータは、以下でした。
1 2 3 4 5 6 7 8 9 10 |
{ "ID": 1, "CreatedAt": "2020-05-23T23:56:23Z", "UpdatedAt": "2020-05-23T23:56:23Z", "DeletedAt": null, "name": "coca cola", "detail": "The coca cola is very very delicious drink", "price": 200, "img": null } |
続いて、PUTメソッドでデータの更新を行います。
更新するデータは、以下です。
1 2 3 4 5 |
{ "name": "Other Somthing Drink", "detail": "Update Other Somthing Drink", "price": 500 } |
Sendボタンを押下します。

1 2 3 4 5 6 7 8 9 10 |
{ "ID": 1, "CreatedAt": "0001-01-01T00:00:00Z", "UpdatedAt": "0001-01-01T00:00:00Z", "DeletedAt": null, "name": "Other Somthing Drink", "detail": "Update Other Somthing Drink", "price": 500, "img": null } |
ID=1のデータが更新されましたね。
MySQLデータの部分更新
PUTメソッドは、リクエスト先のリソースを丸ごと更新する際に使用されるメソッドですが、PATCHメソッドは部分的な更新を担当します(そういった使い分けをしています)。
URLマッピングの追加
1 2 3 4 5 6 7 8 9 10 11 |
// app/url_mappings.go package app import "github.com/gouser/money-boy/api/controllers/products" func mapUrls() { ... router.PATCH("/products/:product_id", products.UpdateProduct) # new } |
コントローラーの修正
ユーザーからのリクエストメソッドの種別を判定する処理を入れます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
// UpdateProduct - Update product func UpdateProduct(c *gin.Context) { productID, productErr := strconv.ParseUint(c.Param("product_id"), 10, 64) if productErr != nil { err := errors.NewBadRequestError("product id should be a number") c.JSON(err.Status, err) return } var product products.Product if err := c.ShouldBindJSON(&product); err != nil { apiErr := errors.NewBadRequestError("invalid json body") c.JSON(apiErr.Status, apiErr) return } product.ID = uint(productID) # new isPartial := c.Request.Method == http.MethodPatch # update result, err := services.UpdateProduct(isPartial, product) if err != nil { c.JSON(err.Status, err) return } c.JSON(http.StatusOK, result) } |
c.Request.MethodにてPATCHメソッドが取得できたら、isPartial変数にはtrueが入るようになります。
この変数をサービス(UpdateProduct)に渡します。
サービスの修正
Controllerの修正に合わせて、サービスも修正します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
// products_service.go package services import ( "github.com/jinzhu/gorm" "github.com/gouser/money-boy/api/domain/products" "github.com/gouser/money-boy/api/utils/errors" ) // UpdateProduct - Service func UpdateProduct(isPartial bool, product products.Product) (*products.Product, *errors.ApiErr) { current, err := GetProduct(product.ID) if err = current.Get(); err != nil { return nil, err } if isPartial { if product.Name != "" { current.Name = product.Name } if product.Detail != "" { current.Detail = product.Detail } if product.Price != 0 { current.Price = product.Price } if product.Img != nil { current.Img = product.Img } if err := current.PartialUpdate(); err != nil { return nil, err } } else { // Change Product Info current.Name = product.Name current.Detail = product.Detail current.Price = product.Price current.Img = product.Img if err := current.Update(); err != nil { return nil, err } } return current, nil } |
PATCHの場合は個別にパラメータを抽出し、PartailUpdateメソッドで部分置換を行う処理を実装しました。
Partial Update処理の追加
最後に部分更新処理を追加します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
// domain/products/product_dao.go package products import ( "github.com/gouser/money-boy/api/datasources/mysql/products_db" "github.com/gouser/money-boy/api/utils/errors" "github.com/gouser/money-boy/api/utils/mysqlutils" ) // PartialUpdate - product func (p *Product) PartialUpdate() *errors.ApiErr { if result := products_db.Client. Table("products"). Where("id IN (?)", p.ID). Updates(&p); result.Error != nil { return mysqlutils.ParseError(result.Error) } return nil } |
Saveメソッドでも対応可能だとは思いますが、別の書き方としてUpdatesメソッドを使用してみました。
Updatesメソッドでは、以下のようなクエリが発行されます。
1 |
UPDATE "products" SET "name" = "p.Name", "detail" = "p.detail", "price" = "p.price", "img" = "p.Img" WHERE "id" = 'p.ID'; |
動作確認
それでは、動作確認をしてみましょう。
コンテナを立ち上げ直してから、Talend API Testerからテストします。
データの一部分を書き換えたいので、nameだけ指定します。
1 2 3 |
{ "name": "PATCH TEST" } |

それでは、リクエスト先に「http://localhost:8080/products/1」を指定し、メソッドを「PATCH」に変更後、Sendボタンを押下しましょう。

1 2 3 4 5 6 7 8 9 10 |
{ "ID": 1, "CreatedAt": "2020-05-23T23:56:23Z", "UpdatedAt": "2020-05-29T20:35:16Z", "DeletedAt": null, "name": "PATCH TEST", "detail": "Update Other Somthing Drink", "price": 500, "img": null } |
戻り値として受け取ったデータを確認してみると、nameがアップデートされたことがわかります。
次回
次回は、MySQLの削除処理について学びましょう。
コメントを残す
コメントを投稿するにはログインしてください。