Go GORM 有很多关系。如何将数据存储到MYSQL DB

Posted

技术标签:

【中文标题】Go GORM 有很多关系。如何将数据存储到MYSQL DB【英文标题】:Go GORM has many relationship. How to store the data to MYSQL DB 【发布时间】:2021-12-28 18:41:06 【问题描述】:

我是后端新手,也是 golang 和 gorm 的新手。

我通过在 go 和 gorm 中使用 has-many 构建简单的 api 来学习后端。

我正在尝试使用“TripId”的外键创建“Trip”表和“SingleTrip”表

基本上,一次旅行有一段singleTrip

这里是代码, 我有两个模型是

type Base struct 
    gorm.Model
    ID string `gorm:"primary_key;not_null" json:"id"`


type Trip struct 
    Base
    UserId     string        `gorm:"not_null" json:"user_id"`
    TripDetail []SingleTrip  `gorm:"foreignKey:Id;constraint:OnUpdate:CASCADE,OnDelete:SET NULL;" json:"trip_detail"`



type SingleTrip struct 
    gorm.Model
    Id          string    `gorm:"primary_id" json:"id"`
    TripId      string    `gorm:"primary_key;not_null" json:"trip_id"`
    TripDate    string    `gorm:"size:255;not_null;" json:"trip_date"`
    FromAddress string    `gorm:"size:255;not_null;" json:"from_address"`
    ToAddress   string    `gorm:"size:255;not_null;" json:"to_address"`
    Distance    float64   `gorm:"not_null;" json:"distance"`
    Reason      string    `gorm:"size:255;not_null;" json:"reason"`
    Comments    string    `gorm:"size:255;not_null;" json:"comments"`
    Receipts    string    `gorm:"size:255;not_null;" json:"receipt"`
    Amount      float64   `gorm:"not_null;" json:"amount"`
    RoundTrip   string    `gorm:"size:255;not_null;" json:"round_trip"`


func (trip *Trip) BeforeCreate(gorm *gorm.DB) error 
    tripId := uuid.New()
    trip.ID = tripId.String()
    return nil


func (singleTrip *SingleTrip) BeforeCreate(gorm *gorm.DB) error 
    id := uuid.New()
    singleTrip.Id = id.String()
    return nil



这是我尝试保存在 mysql db 中的请求正文


    "user_id": "d4be5cbc-4377-11ec-a108-2e7a8ebc414a",
    "trip_detail": [
        
            "trip_date": "05/29/2021",
            "from_address":"from address field",
            "to_address": "to address field",
            "distance": 50.2,
            "reason": "dinner",
            "comments": "this is comments field",
            "receipt": "for lunch",
            "amount": 50.0,
            "round_trip": "round field"
        ,
        
            "trip_date": "07/29/2021",
            "from_address": "some address",
            "to_address": "Some to address",
            "distance": 50.2,
            "reason": "dinner",
            "comments": "this is  comment field",
            "receipt": "for meeting",
            "amount": 50.0,
            "round_trip": "round field"
        
    ]


这是 SaveTrip 方法

func (trip *Trip) SaveTrip(db *gorm.DB) (*Trip, error) 
    err := db.Debug().Create(&trip).Error
    if err != nil 
        return &Trip, err
    

    return trip, nil


控制器 trip_controller.go

func (server *Server) CreateTrip(context *gin.Context) 
    errMessage := map[string]string
    body, err := ioutil.ReadAll(context.Request.Body)

    trip := models.Trip

    if err != nil 
        errMessage["Invalid Request"] = "Invalid Request"
        context.JSON(http.StatusUnprocessableEntity, gin.H
            "status": http.StatusUnprocessableEntity,
            "error":  errMessage,
        )
        return
    

    err = json.Unmarshal([]byte(body), &trip)
    if err != nil 
        errMessage["Unable to Unmarshall"] = "Unable to unmarshal"
        context.JSON(http.StatusUnprocessableEntity, gin.H
            "status": http.StatusUnprocessableEntity,
            "error":  errMessage,
        )
        return
    

    trip.Prepare()
    validatingErrMessage := map[string]string
    validatingErrMessage = trip.ValidatingTripData("create")

    if len(validatingErrMessage) > 0 
        errMessage = validatingErrMessage
        context.JSON(http.StatusUnprocessableEntity, gin.H
            "status": http.StatusUnprocessableEntity,
            "error":  errMessage,
        )
        return
    

    createdTrip, err := trip.SaveTrip(server.DB)

    if err != nil 
        formattedError := utils.FormatError(err.Error())
        context.JSON(http.StatusInternalServerError, gin.H
            "status": http.StatusInternalServerError,
            "error":  err.Error(),
        )
        return
    

    context.JSON(http.StatusOK, gin.H
        "status":   http.StatusOK,
        "response": createdTrip,
    )



这是我从邮递员那里得到的回复


    "response": 
        "ID": 0,
        "CreatedAt": "2021-11-18T00:08:55.357+05:30",
        "UpdatedAt": "2021-11-18T00:08:55.357+05:30",
        "DeletedAt": null,
        "id": "52a44faa-437a-4695-82c7-b45cdab9e6d1",
        "user_id": "d4be5cbc-4377-11ec-a108-2e7a8ebc414a",
        "trip_detail": [
            
                "ID": 0,
                "CreatedAt": "2021-11-18T00:08:55.362+05:30",
                "UpdatedAt": "2021-11-18T00:08:55.362+05:30",
                "DeletedAt": null,
                "Id": "52a44faa-437a-4695-82c7-b45cdab9e6d1",
                "trip_date": "05/29/2021",
                "from_address": "some address",
                "to_address": "some - to address",
                "distance": 50.2,
                "reason": "dinner",
                "comments": "this is comments field",
                "receipt": "for lunch",
                "amount": 50,
                "round_trip": "round field"
            ,
            
                "ID": 0,
                "CreatedAt": "2021-11-18T00:08:55.362+05:30",
                "UpdatedAt": "2021-11-18T00:08:55.362+05:30",
                "DeletedAt": null,
                "Id": "52a44faa-437a-4695-82c7-b45cdab9e6d1",
                "trip_date": "07/29/2021",
                "from_address": "some address",
                "to_address": "Some to address",
                "distance": 50.2,
                "reason": "dinner",
                "comments": "this is  comment field",
                "receipt": "for meeting",
                "amount": 50,
                "round_trip": "round field"
            
        ]
    ,
    "status": 200


但数据没有存储在数据库中

当尝试将数据存储到数据库时,我遇到了错误

如何将数据存储在Db中,或者有没有其他方法可以存储值?

我正在使用 golang、gorm、gin 框架。

【问题讨论】:

【参考方案1】:

对不起,如果我语法不好。

更改这行代码

func (singleTrip *SingleTrip) BeforeCreate(gorm *gorm.DB) error 
    /*
        fmt.Println(singleTrip.Id) // this value will be Trip (parent) ID
    */

    id := uuid.New()

    // WRONG
    // singleTrip.Id = id.String()

    /* 
        Maybe This is what you want
        TripId is the primary Key
        not Id (from how you define the model tag)
    */
    singleTrip.TripId = id.String() 
    return nil

SingleTrip 中的Id (在这种情况下,是外键,而不是主键)不能被BeforeCreate 覆盖,因为gorm 关系已经处理了来自Trip 的Id 值为ID (父母)。如果需要,您可以尝试记录该值。

这将导致 MySQL 本身出错,因为在事务中新的 id 没有记录在父 (Trip) 主键中。

还有一点建议,明智地选择外键名称,例如 trip_id 适合 Trip x SingleTrip 外键而不是 Id。刚开始理解模型很混乱。

完整代码:

package main

import (
    "encoding/json"
    "fmt"
    "io/ioutil"
    "net/http"

    "github.com/gin-gonic/gin"
    "github.com/google/uuid"
    "gorm.io/driver/mysql"
    "gorm.io/gorm"
    "gorm.io/gorm/logger"
)

var DB *gorm.DB

func main() 
    databaseConfig := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?multiStatements=true&parseTime=true", "root", "", "127.0.0.1", "3306", "tester")

    DB, _ = gorm.Open(mysql.Open(databaseConfig), &gorm.Config
        Logger: logger.Default.LogMode(logger.Info),
    )
    sqlDB, _ := DB.DB()
    defer sqlDB.Close()

    DB.AutoMigrate(&Trip, &SingleTrip)

    router := gin.Default()

    router.POST("/", CreateTrip)

    router.Run(":8080")


type Base struct 
    gorm.Model
    ID string `gorm:"primary_key;not_null" json:"id"`


type Trip struct 
    // Base
    gorm.Model
    ID         string       `gorm:"size:255;primary_key;not_null" json:"id"`
    UserId     string       `gorm:"not_null" json:"user_id"`
    TripDetail []SingleTrip `gorm:"foreignKey:Id;constraint:OnUpdate:CASCADE,OnDelete:SET NULL;" json:"trip_detail"`


type SingleTrip struct 
    gorm.Model
    Id          string  `gorm:"size:255;primary_id" json:"id"`
    TripId      string  `gorm:"primary_key;not_null" json:"trip_id"`
    TripDate    string  `gorm:"size:255;not_null;" json:"trip_date"`
    FromAddress string  `gorm:"size:255;not_null;" json:"from_address"`
    ToAddress   string  `gorm:"size:255;not_null;" json:"to_address"`
    Distance    float64 `gorm:"not_null;" json:"distance"`
    Reason      string  `gorm:"size:255;not_null;" json:"reason"`
    Comments    string  `gorm:"size:255;not_null;" json:"comments"`
    Receipts    string  `gorm:"size:255;not_null;" json:"receipt"`
    Amount      float64 `gorm:"not_null;" json:"amount"`
    RoundTrip   string  `gorm:"size:255;not_null;" json:"round_trip"`


func (trip *Trip) BeforeCreate(gorm *gorm.DB) error 
    tripId := uuid.New()
    trip.ID = tripId.String()
    return nil


func (singleTrip *SingleTrip) BeforeCreate(gorm *gorm.DB) error 
    /*
        fmt.Println(singleTrip.Id) // this value will be Trip (parent) ID
    */

    id := uuid.New()

    // WRONG
    // singleTrip.Id = id.String()

    /* 
        Maybe This is what you want
        TripId is the primary Key
        not Id (from how you define the model tag)
    */
    singleTrip.TripId = id.String() 
    return nil


func (trip *Trip) SaveTrip(db *gorm.DB) (*Trip, error) 
    err := db.Debug().Create(&trip).Error
    if err != nil 
        return &Trip, err
    

    return trip, nil


// =======================================================
func CreateTrip(context *gin.Context) 
    errMessage := map[string]string
    body, err := ioutil.ReadAll(context.Request.Body)

    trip := Trip

    if err != nil 
        errMessage["Invalid Request"] = "Invalid Request"
        context.JSON(http.StatusUnprocessableEntity, gin.H
            "status": http.StatusUnprocessableEntity,
            "error":  errMessage,
        )
        return
    

    err = json.Unmarshal([]byte(body), &trip)
    if err != nil 
        errMessage["Unable to Unmarshall"] = "Unable to unmarshal"
        context.JSON(http.StatusUnprocessableEntity, gin.H
            "status": http.StatusUnprocessableEntity,
            "error":  errMessage,
        )
        return
    

    fmt.Printf("Trip: %+v", trip)
    // fmt.Printf("Trip: %+v", trip.TripDetail[0].ID)
    // fmt.Printf("Trip: %+v", trip.TripDetail[0].Id)

    // trip.Prepare()
    // validatingErrMessage := map[string]string
    // validatingErrMessage = trip.ValidatingTripData("create")

    // if len(validatingErrMessage) > 0 
    //  errMessage = validatingErrMessage
    //  context.JSON(http.StatusUnprocessableEntity, gin.H
    //      "status": http.StatusUnprocessableEntity,
    //      "error":  errMessage,
    //  )
    //  return
    // 

    createdTrip, err := trip.SaveTrip(DB)

    if err != nil 
        // formattedError := utils.FormatError(err.Error())
        context.JSON(http.StatusInternalServerError, gin.H
            "status": http.StatusInternalServerError,
            "error":  err.Error(),
        )
        return
    

    context.JSON(http.StatusOK, gin.H
        "status":   http.StatusOK,
        "response": createdTrip,
    )

Output

【讨论】:

以上是关于Go GORM 有很多关系。如何将数据存储到MYSQL DB的主要内容,如果未能解决你的问题,请参考以下文章

Go语言学习之旅--gorm

Go语言学习之旅--gorm

Go语言学习之旅--gorm

Go开源世界主流成熟ORM框架gorm实践分享

从java到Go搭建Go的ORM框架Gorm

从java到Go搭建Go的ORM框架Gorm