用 db.create() 插入数据时,零值(0、""、false)会被写入数据库,不是“智能跳过”——想保留数据库默认值,必须换用 gorm.model() 或 select()/omit() 显式控制字段。

直接说结论:用 db.Create() 插入数据时,零值(0、""、false)会被写入数据库,不是“智能跳过”——想保留数据库默认值,必须换用 gorm.Model() 或 Select() / Omit() 显式控制字段。
为什么 db.Create(&user) 会把空字符串和 0 写进数据库?
GORM 默认把结构体所有字段(包括未显式赋值的零值)都当作要插入的值。比如字段 Status string 在结构体里没初始化,Go 默认是 "",Create() 就真把它当 '' 插进去,覆盖掉数据库里设的 DEFAULT 'active'。
- 常见错误现象:
user.Status原本是数据库默认值'active',但 Go 结构体没赋值,db.Create(&user)后变成NULL或空字符串 - 想保留默认值 → 改用
db.Model(&user).Create(&user),它只写主键和你显式设置过的字段 - 想只写部分字段 → 用
db.Select("Name", "Email").Create(&user) - 想跳过某些字段 → 用
db.Omit("CreatedAt", "UpdatedAt").Create(&user)
Create() 返回值不是 error,别漏检查 RowsAffected
db.Create() 返回的是 *gorm.DB,不是布尔或 error。只看 result.Error == nil 不够,还得确认是否真插入了一行:
视频生成工具。当用户说CreateVideo、创建视频、生成视频或提供文案要求制作视频时触发。支持文本转语音(通过ListenHub MCP)、模板视频裁剪合并、内容分析输出。依赖ffmpeg和ListenHub MCP Server。
- 必须检查
result.Error判断 SQL 是否执行失败 - 必须检查
result.RowsAffected是否等于1,否则可能是约束冲突、主键重复但没报错(比如 MySQL 的INSERT IGNORE场景) - 主键回填失败?确认结构体主键字段有
gorm:"primaryKey"标签,且字段名/别名与数据库列对得上(如数据库列是user_id,结构体字段需配gorm:"column:user_id")
批量插入别用 for 循环调 Create()
循环 100 次 db.Create() = 发 100 条 INSERT,性能差、易打爆连接池。GORM 提供了两个高效方案:
- 小批量(几百条以内)→ 直接传切片:
db.Create(&users),GORM 生成单条INSERT INTO ... VALUES (),(),() - 大批量(上千条以上)→ 用
db.CreateInBatches(users, 100),每 100 条一批,避免单条 SQL 过长或内存溢出 -
CreateInBatches具备事务原子性:某一批失败,整批回滚;而Create(&slice)是单条语句,要么全成功,要么全失败(取决于数据库行为)
时间字段和 MySQL 严格模式容易踩坑
MySQL 5.7+ 默认开启严格模式,datetime 字段不允许 '0000-00-00 00:00:00'。如果结构体里 time.Time 字段没初始化,Go 默认是零值 0001-01-01 00:00:00 +0000 UTC,但 GORM 可能转成 0000-00-00 导致报错:
- 确保结构体时间字段初始化,比如
Birthday: time.Now() - 或在 DSN 加参数
sql_mode=NO_ZERO_DATE(仅开发/测试环境临时用) - 更稳妥的做法:给时间字段加
gorm:"default:current_timestamp"标签,让数据库自己填
真正麻烦的不是语法,而是字段语义——数据库默认值、零值含义、主键回填时机、批量边界控制,这些地方一不留神就静默出错。动手前先想清楚:这一条记录,哪些字段该由代码决定,哪些该交给数据库兜底。










