beego orm默认字段不允许为空,必须显式添加orm:"null"标签;string类型需用*string才能存null,查询null须用__isnull操作符。

空字段默认不允许为空,必须显式声明 null
Beego ORM 默认所有 struct 字段映射到数据库时都加 NOT NULL 约束。哪怕你没写任何 tag,Name string 也会生成 name VARCHAR(255) NOT NULL。这和很多 ORM(比如 GORM)的“零值即空”逻辑不同,容易在插入或更新时直接报错:Error 1048: Column 'xxx' cannot be null。
解决方法只有一条:对可能为空的字段,必须加 orm:"null" tag:
-
Name string `orm:"null"`→ 允许为 NULL -
Age int `orm:"null"`→ 注意:int 类型加null后,数据库列会变成INT NULL,但 Go 中读取时若 DB 值为 NULL,Age仍会被赋零值(0),不会自动转成指针;如需区分“未设置”和“设为 0”,得用*int - 时间字段同理:
UpdatedAt time.Time `orm:"null;type(datetime)"`
default 和 null 能共存,但语义要分清
default(0) 和 null 不冲突,但组合后行为易被误解。例如:
Score float64 `orm:"null;default(0.0)"`
这表示:DB 列是 FLOAT NULL DEFAULT 0.0,即插入时不提供该字段,DB 自动填 0;但如果显式传 nil(比如用 map[string]interface{} 插入),则存为 NULL。
常见误用:
- 写了
default(1)却没写null→ 字段仍是NOT NULL,default 只在 INSERT 时无值才生效,UPDATE 时不能设为 NULL - 想让字段“可空且有默认”,却只写
default(1)→ 实际无法插入 NULL,会报错
空字符串 "" 不等于 NULL,string 字段慎用 null
MySQL 中 ""(空字符串)和 NULL 是两个完全不同的值。Beego ORM 对 string 字段不作自动转换 —— 你传 "",它就插空字符串;你传 nil(不可能,因为 string 是值类型),它根本收不到 nil。
所以:orm:"null" 对 string 字段的实际意义,是允许你在调用 o.Insert() 或 o.Update() 前,把该字段设为 "" 并期望它进 DB 成 NULL?不行。Beego ORM 不识别空字符串为 NULL。
正确做法只有两种:
- 改用
*string类型:Name *string `orm:"null"`,然后插入前设user.Name = nil,才能真正存 NULL - 保持
string类型,接受空字符串语义,去掉null,靠业务逻辑判断""表示“未填写”
QuerySeter 查询时,IsNil 和 IsNotNull 才能匹配 NULL
用 QueryTable 查 NULL 字段,不能写 Filter("name", "") 或 Filter("name__exact", nil) —— 都无效。
必须用专门的操作符:
-
qs.Filter("name__isnull", true)→WHERE name IS NULL -
qs.Filter("name__isnull", false)→WHERE name IS NOT NULL -
qs.Exclude("name__isnull", true)等价于上面第二条
注意:name__isnull 中的字段名必须和 struct tag 里的 column(xxx) 一致,否则查不到;如果没设 column,则用字段名小写(如 Name → name)。
最常被忽略的一点:空字段处理不是“配个 tag 就完事”,而是要通盘考虑类型选择(string 还是 *string)、INSERT/UPDATE 时的赋值方式、以及后续查询时的谓词写法——三者不配套,就会出现“以为存了 NULL,结果查出来全是空字符串”或者“死活查不到那条记录”。











