使用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
相关产品推荐
相关产品推荐

