gorm:"default:xxx"仅设置数据库default约束,不干预go层零值;非指针字段插入时会显式传零值覆盖默认值,需用指针或scanner/valuer接口配合;automigrate不修改已有字段默认值。

直接在结构体字段的 gorm tag 里写 default 是最常用的方式,但要注意它只影响数据库层面的默认值(即建表时的 DEFAULT 约束),不控制 Go 层对象初始化行为;真正生效还得看字段类型、零值处理和迁移时机。
用 default tag 定义数据库默认值
这是最直观的做法,GORM 会在 AutoMigrate 时把该值写进表结构的 DEFAULT 子句:
-
default值必须是数据库能解析的字面量,比如"pending"、18、CURRENT_TIMESTAMP - 字符串要用单引号或双引号包裹(取决于驱动),例如
gorm:"default:'draft'" - 时间字段推荐用
CURRENT_TIMESTAMP而不是 Go 的time.Now(),后者不会被识别为 SQL 表达式 - 注意:MySQL 5.7+ 对
TEXT/BLOB类型不支持默认值,会静默忽略
type Order struct {
ID uint `gorm:"primaryKey"`
Status string `gorm:"default:'pending'"`
CreatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP"`
Amount float64 `gorm:"default:0.00"`
}
Go 层默认值要靠指针或自定义 Scan/Value
如果字段是 int、string 这类非指针类型,新建结构体实例时字段就是零值(0、""),哪怕数据库有 DEFAULT,GORM 插入时也会显式传入零值,覆盖掉数据库默认逻辑。
- 解决办法之一:把字段改成指针类型,如
*string、*int,这样新实例中值为nil,GORM 会跳过该字段,让数据库用DEFAULT - 更通用的办法:实现
Scanner和Valuer接口,在Scan时补零值,在Value时判断是否为零值再决定是否写入 - 别依赖
defaulttag 来“修复”业务层逻辑 —— 它不是 Go 初始化机制,只是 DDL 提示
批量插入时 default 不起作用的常见原因
用 CreateInBatches 或原生 INSERT ... VALUES 批量写入时,GORM 默认会把所有字段都显式列出,包括那些值为零值的字段。此时即使数据库有 DEFAULT,也不会触发。
- 确认是否启用了
AllowGlobalUpdate或误用了Select限制字段,导致 GORM 强制写出全部字段 - 想让默认值生效,得确保对应字段在 INSERT 语句中被省略 —— 只有指针为
nil或使用Select明确排除时才会发生 - 测试方法:开启 GORM 日志(
logger.Default.LogMode(logger.Info)),看生成的 SQL 是否含该字段
自动迁移后改 default tag 没效果
AutoMigrate 只创建或新增字段,不会修改已有字段的 DEFAULT 约束。改了 tag 再跑一次,GORM 不会执行 ALTER COLUMN ... SET DEFAULT。
- MySQL 需手动执行
ALTER TABLE orders ALTER status SET DEFAULT 'shipped'; - PostgreSQL 要用
ALTER COLUMN status SET DEFAULT 'shipped'; - GORM 本身不提供跨方言的 default 修改能力,别指望
AutoMigrate自动同步 - 上线前务必检查表结构和代码 tag 是否一致,靠迁移脚本或 DBA 工具比依赖 GORM 更可靠
最容易被忽略的是:default 是数据库概念,不是 Go 初始化逻辑;你看到的“默认值没生效”,八成是因为字段被显式赋了零值,而不是数据库没设好。











