Go语言控制器中如何将记录保存至两个关联数据表
问题分析与解决方案
核心错误点
- 错误实例化Horary对象:创建排班数据时误用
models.Employe替代models.Horary,导致数据未写入horary表,反而错误存入employee表,无法获取有效Horary ID关联员工。 - 结构体外键配置错误:
Employe中PinEmploye的FOREIGNKEY标签属于无效配置,且ScheduleId缺少明确的外键关联标签,GORM无法识别其与Horary表的关联关系。 - 请求字段解析失效:
Employe结构体中的Arrival和Departure被注释,JSON解析时无法获取这两个字段值,创建Horary会传入空时间,可能触发数据库约束错误。 - 错误处理不当:
log.Fatal会直接终止整个服务,不适用于HTTP请求场景,应改为日志记录而非进程退出。
修正后的代码
1. 修正结构体定义
type Employe struct { ID int `json:"id" gorm:"primaryKey;autoIncrement"` PinEmploye string `json:"pinEmploye"` // 移除错误的FOREIGNKEY标签 FirstName string `json:"first_name"` LastName string `json:"last_name"` Company string `json:"company"` Position string `json:"position"` ScheduleId int `json:"schedule_id" gorm:"foreignKey:ScheduleId;references:ID"` // 明确关联Horary的ID Arrival time.Time `json:"arrival"` // 取消注释以接收请求中的排班时间 Departure time.Time `json:"departure"` // 取消注释 CreatedAt time.Time `json:"fechacreacion" gorm:"autoCreateTime"` // 自动生成创建时间 } type Horary struct { ID int `json:"id" gorm:"primaryKey;autoIncrement"` Arrival time.Time `json:"arrival"` Departure time.Time `json:"departure"` }
2. 修正控制器Save函数
func Save(writer http.ResponseWriter, request *http.Request) { employe := models.Employe{} db := common.GetConnection() // 解析请求体 err := json.NewDecoder(request.Body).Decode(&employe) if err != nil { log.Printf("解析请求体失败: %v", err) common.SendError(writer, http.StatusBadRequest) return } // 生成员工PIN randomBytes := make([]byte, 2) _, err = rand.Read(randomBytes) if err != nil { log.Printf("生成PIN失败: %v", err) common.SendError(writer, http.StatusBadRequest) return } employe.PinEmploye = hex.EncodeToString(randomBytes) // 正确实例化Horary并保存 horary := models.Horary{ Arrival: employe.Arrival, Departure: employe.Departure, } if err = db.Create(&horary).Error; err != nil { log.Printf("保存排班信息失败: %v", err) common.SendError(writer, http.StatusInternalServerError) return } // 关联排班ID到员工并保存 employe.ScheduleId = horary.ID if err = db.Create(&employe).Error; err != nil { log.Printf("保存员工信息失败: %v", err) common.SendError(writer, http.StatusInternalServerError) return } // 返回响应 respJson, _ := json.Marshal(employe) common.SendResponse(writer, http.StatusCreated, respJson) fmt.Println(employe) }
额外优化建议
- 事务一致性保障:将员工和排班的保存操作包裹在GORM事务中,避免单步失败导致数据不一致:
tx := db.Begin() defer func() { if r := recover(); r != nil { tx.Rollback() } }() if err := tx.Create(&horary).Error; err != nil { tx.Rollback() log.Printf("保存排班失败: %v", err) common.SendError(writer, http.StatusInternalServerError) return } employe.ScheduleId = horary.ID if err := tx.Create(&employe).Error; err != nil { tx.Rollback() log.Printf("保存员工失败: %v", err) common.SendError(writer, http.StatusInternalServerError) return } tx.Commit() - 时间格式适配:若请求时间格式非Go默认的RFC3339,需指定自定义解析格式,或在结构体标签中添加
gorm:"type:datetime"确保数据库字段匹配。 - 调试友好性:开发环境下可在错误响应中返回具体错误信息,便于快速定位问题。
内容的提问来源于stack exchange,提问作者Edwin Fernando Pirajan Arevalo
相关产品推荐
相关产品推荐

