gorm:"default"标签必须显式声明才生效,空值字段是否被忽略取决于该标签而非字段是否有值;指针类型不自动跳过零值,需配合default:null等标签;最可控方式是用Select()显式指定插入字段。

gorm:default 标签必须显式声明才生效
空值字段(如 int 为 0、string 为空串、*time.Time 为 nil)能否被忽略,不取决于字段是否“有值”,而取决于该字段是否带 gorm:"default" 或更完整的 gorm:"default:xxx" 标签。GORM 不会自动把零值当作“未设置”而跳过——它只认这个标签。
-
gorm:"default:CURRENT_TIMESTAMP":插入时数据库生成默认值,Go 层传 0 或空串都不影响 -
gorm:"default:null":字段允许 NULL,且 Go 层传零值(如int为 0)时,GORM 会将其转为 SQL 的NULL而非字面量 0 - 没写
default标签 → 即使字段是*string且为 nil,GORM 仍可能插''(空字符串)或0,而非跳过该字段
指针类型字段 ≠ 自动忽略零值
很多人误以为用 *string、*int64 就能天然跳过空值,其实不行。GORM 对指针的处理逻辑是:如果指针为 nil,且字段有 gorm:"default:null",才会生成 NULL;否则仍可能按零值插入(比如 *int64 为 nil 但没配 default,GORM 可能 fallback 到 0)。
- 正确组合:
Name *string `gorm:"default:null"`+ 插入时设为nil→ SQL 中该列不出现或为 NULL - 危险组合:
Age *int64(无 tag)+Age: nil→ GORM 可能忽略该字段,也可能插 0,行为不稳定,依赖版本和驱动 - MySQL 中
TINYINT字段若接收 0,即使 Go 是*int8且为 nil,也容易因类型隐式转换导致误插
批量创建(CreateInBatches / Create)时,空值处理逻辑与单条一致
CreateInBatches 和 Create 都复用同一套字段序列化逻辑,不会因为“批量”就额外跳过零值字段。是否忽略,完全由结构体 tag 和字段当前值共同决定。
- 所有记录共享同一套 struct 定义,所以
gorm:"default"必须在模型定义里写死,不能运行时动态改 - 若一批数据中部分记录某字段为 nil、部分为非 nil,只要 tag 合理,GORM 会为每条记录单独判断该字段是否参与 INSERT
- 注意 MySQL 严格模式:如果表已有字段定义为
NOT NULL且无默认值,而你又试图跳过它(比如指针为 nil 且没配default:null),会直接报错ERROR 1364 (HY000): Field 'xxx' doesn't have a default value
想真正跳过字段?得用 Select() 显式指定列
最可控的方式不是靠“空值自动忽略”,而是用 Select() 明确告诉 GORM 哪些字段要进 INSERT 语句。这对批量场景尤其有效,且不受零值干扰。
-
db.Select("name", "email").Create(&user):只插 name 和 email,其他字段无论是否空、是否有 default,全不出现 -
db.Select("name", "email").CreateInBatches(users, 100):同理,每条记录都只含指定字段 - 缺点:无法复用模型上定义的
default行为,所有默认逻辑需由应用层保证
空值是否被忽略,从来不是“有没有值”的问题,而是“有没有告诉 GORM 该怎么处理这个值”的问题。漏掉 default 标签、混淆指针语义、或依赖批量接口的隐式优化,都会让实际插入结果偏离预期。











