default标签仅设置数据库级默认值,由automigrate生成default子句,insert时数据库填充,不影响go层初始化;业务默认值应在go层用构造函数或回调处理。

字段定义时用 default 标签设置数据库级默认值
数据库建表时生效的默认值,由 default 标签控制,GORM 会在迁移(AutoMigrate)时生成 DEFAULT SQL 子句。这个值在 INSERT 语句不包含该字段时由数据库填充,与 Go 层逻辑无关。
常见错误是以为写上 default 就能在 Go 结构体初始化时自动赋值——它不会影响 Go 变量的零值,也不会触发 GORM 的预处理逻辑。
-
default值必须是数据库能直接解析的字面量,比如"CURRENT_TIMESTAMP"、"0"、"false",不能是 Go 表达式或函数调用 - MySQL 中
datetime字段用default:"CURRENT_TIMESTAMP";PostgreSQL 需写成default:"now()" - 字符串类型慎用
default:"",某些数据库(如 MySQL 严格模式)可能拒绝空字符串插入,建议显式设为NULL并配合nullable
结构体初始化时用 Go 零值或构造函数控制应用层默认值
GORM 不会主动覆盖你代码中已赋值的字段,但如果你依赖结构体字段的初始值(比如 CreatedAt time.Time),就得自己确保它非零。Go 的 time.Time{} 是零值(1年1月1日),显然不是你想要的“创建时间”。
更可靠的做法是在创建实例时显式赋值,或封装构造函数:
func NewUser(name string) *User {
return &User{
Name: name,
Status: "active",
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
}
- 不要依赖
default标签来替代业务逻辑中的必填字段赋值 - 如果字段有业务含义的默认值(如状态为
"pending"),应在 Go 层初始化,而非交给数据库兜底 - GORM 的
BeforeCreate回调可用于统一注入,但要注意:它只在Create时触发,Save或批量插入可能绕过
Default 和 DefaultValue 的区别容易混淆
GORM v2 中已废弃 DefaultValue(v1 语法),现在统一用 default 标签。但仍有两个易混点:
-
default是数据库默认值(SQL 层);default值为空字符串(default:"")在多数驱动中等价于DEFAULT NULL,并非插入空串 - 想让 GORM 在 INSERT 时不发送该字段(从而触发数据库默认),字段值必须为零值且未被显式赋值;若字段是指针(如
*string),nil 才算未赋值 - 对
uint、int等数值类型,零值是0,若业务中0是合法值,就不能靠零值触发数据库默认——此时必须用指针类型或回调
时间字段的默认行为最常踩坑
GORM 对 CreatedAt、UpdatedAt 有特殊约定,但仅当字段名完全匹配且类型为 time.Time 或其别名时才自动处理。一旦改名(如 CreatedTime)或用指针(*time.Time),自动填充就失效。
- 启用自动时间戳需确保字段名为
CreatedAt、UpdatedAt、DeletedAt(软删除),且类型为time.Time - 自定义时间字段名?用
autoCreateTime/autoUpdateTime标签,例如:MyCreatedAt time.Time `gorm:"autoCreateTime"` - MySQL 8.0+ 支持
TIMESTAMP列的CURRENT_TIMESTAMP默认,但 GORM 迁移时若字段类型是time.Time,会默认映射为DATETIME,得手动加type:timestamp标签
default 标签,迟早会在数据导入、直连 SQL 或跨库迁移时出问题。golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











