You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用GORM在Go语言中无法将数据保存到两张关联表的问题

问题分析与解决办法

1. 关联关系定义错误

你的User与UserAddress的关联标签设置逻辑有误:

  • UserAddress缺少专门关联User的外键字段,当前用自身主键Id作为外键不符合关联设计规范,应该新增UserID字段存储关联的用户ID。
  • User结构体中Address的关联标签需要对应正确的外键与引用字段。

修正后的结构体:

type User struct {
    UserId          string        `json:"user_id" gorm:"column:user_id;not null;primarykey"`
    ClientID        string        `json:"cliend_id" gorm:"unique;type:varchar(100);not null"`
    SecertKey       string        `json:"secert_key" gorm:"unique;type:varchar(100);not null"`
    ClientSecretKey string        `json:"client_secert_key" gorm:"unique;type:varchar(100);not null"`
    FirstName       string        `json:"first_name" gorm:"varchar(100);not null"`
    LastName        string        `json:"last_name" gorm:"varchar(100);not null"`
    // 修正关联标签:foreignKey指定UserAddress的外键,references指定User的主键
    Address         []UserAddress `json:"address" gorm:"foreignKey:UserID;references:UserId"`
    Email           string        `json:"email" gorm:"column:email;not null"`
    Phone           string        `json:"phone_no" gorm:"column:phone_no;not null"`
    CreatedDate     time.Time     `json:"created_date" gorm:"column:created_date"`
    UpdatedAt       time.Time     `json:"updated_at" gorm:"column:updated_at"`
}

type UserAddress struct {
    Id      string `json:"id" gorm:"column:id;not null;primaryKey"`
    // 新增外键字段,关联User的UserId
    UserID  string `json:"user_id" gorm:"column:user_id;not null"`
    Street1 string `json:"street1" gorm:"column:street1"`
    Street2 string `json:"street2" gorm:"column:street2"`
    City    string `json:"city" gorm:"column:city"`
    State   string `json:"state" gorm:"column:state"`
    Country string `json:"country" gorm:"column:country"`
}

2. 保存逻辑未触发关联数据写入

GORM的Save方法默认不会自动保存关联的子数据,可通过以下两种方式解决:

方法一:用Create指定关联字段保存

修改Create方法,使用Create并明确指定要保存的关联字段:

func (r *UserRepo) Create(user *models.User) errors.RestAPIError {
    db := r.DB
    fmt.Println("BD", user)
    // 通过Select指定要保存的主表字段及关联的Address
    if err := db.Table(models.TABLE_USER).Create(&user).Select("Address").Error; err != nil {
        fmt.Println(err)
        return errors.NewInternalServerError("Cannot save user in db")
    }
    return errors.NO_ERROR()
}

方法二:事务手动保存关联数据

如果需要更精确的控制,可通过事务先保存用户,再批量保存地址:

func (r *UserRepo) Create(user *models.User) errors.RestAPIError {
    db := r.DB
    tx := db.Begin()
    defer func() {
        if r := recover(); r != nil {
            tx.Rollback()
        }
    }()

    // 保存用户
    if err := tx.Table(models.TABLE_USER).Create(&user).Error; err != nil {
        tx.Rollback()
        fmt.Println(err)
        return errors.NewInternalServerError("Cannot save user in db")
    }

    // 给地址设置关联的用户ID,批量保存
    for i := range user.Address {
        user.Address[i].UserID = user.UserId
    }
    if err := tx.Table(models.TABLE_USER_ADDRESS).Create(&user.Address).Error; err != nil {
        tx.Rollback()
        fmt.Println(err)
        return errors.NewInternalServerError("Cannot save user address in db")
    }

    // 提交事务
    if err := tx.Commit().Error; err != nil {
        fmt.Println(err)
        return errors.NewInternalServerError("Failed to commit transaction")
    }
    return errors.NO_ERROR()
}

3. 后续注意事项

  • 修改结构体后,需重新执行迁移语句更新数据库表结构:
db.Table(models.TABLE_USER).AutoMigrate(&models.User{})
db.Table(models.TABLE_USER_ADDRESS).AutoMigrate(&models.UserAddress{})
  • 测试时无需将UserAddress的Id设置为与User相同的值,保持地址自身主键的独立性更合理。

内容的提问来源于stack exchange,提问作者Yogi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 04:05:37