Beego ORM原生SQL查询分两步:先Raw()构建查询集,再调用QueryRow()等方法执行并映射;QueryRow()查单条,需结构体指针且字段公开、NULL用指针类型;QueryRows()查多条,只接受结构体切片指针;Values()/ValuesList()返回字符串型松散数据;Exec()仅用于增删改,不可查数据。

Beego ORM 的原生 SQL 查询不是“执行完就返回结果”,而是分两步:先用 Raw() 构建查询集,再调用具体方法触发执行并映射。直接调用 Raw("SELECT ...") 不会查库,这点最容易被忽略。
怎么用 Raw() + QueryRow() 查单条记录
QueryRow() 适合明确只取一行、且字段能严格对应结构体字段的场景。它会把结果按列顺序或列名(取决于驱动)填充到传入的结构体指针中。
- 结构体字段必须有公开首字母,且建议加
ormtag 显式指定列名,避免大小写或下划线不匹配(如数据库字段user_name对应结构体字段UserName,tag 写`orm:"column(user_name)"`) - 如果 SQL 中用了别名(如
SELECT id AS uid),结构体字段名或 tag 必须匹配别名,而不是原始列名 - NULL 值字段在结构体中需用指针类型(如
*string、*int)或sql.NullString等,否则会 panic - 示例:
type User struct { ID int `orm:"column(id)"` UserName string `orm:"column(user_name)"` } var u User err := o.Raw("SELECT id, user_name FROM user WHERE id = ?", 1).QueryRow(&u)
怎么用 Raw() + QueryRows() 查多条记录
QueryRows() 是批量映射到结构体切片的标准方式,但注意:它从 Beego 1.1.0 起已取消对多个独立结构体参数的支持(即不能传 &u1, &u2),只接受一个切片指针。
- 切片元素类型必须是结构体(不能是基础类型或 map),且结构体定义和 tag 规则与
QueryRow()完全一致 - 返回值是实际查到的行数(
num)和 error;即使num == 0,err也可能为nil,需靠num判断是否为空结果 - 不要假设切片长度等于行数——它只是输入容器,ORM 会重置并填充内容,所以初始化时用
make([]User, 0)即可 - 示例:
var users []User num, err := o.Raw("SELECT id, user_name FROM user WHERE status = ?", 1).QueryRows(&users)
怎么用 Values() / ValuesList() 获取松散结构数据
当不想定义结构体、或字段动态不确定(比如聚合查询、跨表拼字段)、或只需要部分字段字符串值时,Values() 和 ValuesList() 更灵活。
-
Values()返回[]orm.Params,每个orm.Params是map[string]string,key 是列名(或别名),value 是字符串格式值;NULL 字段值为"" -
ValuesList()返回[]orm.ParamsList,每个orm.ParamsList是[]string,按 SELECT 子句顺序排列;同样,NULL 对应空字符串 - 它们不依赖结构体,也不做类型转换,所有值都是
string,后续需手动strconv.Atoi或time.Parse等处理 - 示例:
var maps []orm.Params _, err := o.Raw("SELECT id, user_name FROM user LIMIT 2").Values(&maps) // maps[0]["id"] → "123"(string)
Exec() 不能查数据,QueryRow/QueryRows 才能
Exec() 只用于 INSERT/UPDATE/DELETE,返回 sql.Result,提供 RowsAffected() 和 LastInsertId()。若误用它执行 SELECT,不会报错,但得不到任何结果数据——这是新手最常踩的坑。
- 哪怕 SQL 语法合法(如
o.Raw("SELECT 1").Exec()),也只会返回RowsAffected() == 0,不解析结果集 - 想查数据,必须用
QueryRow()、QueryRows()、Values()或ValuesList()之一 - 如果需要同时执行修改 + 查询(如 INSERT 后查新 ID),得拆成两步:先
Exec(),再用QueryRow()单独查
真正容易被绕晕的点在于:RawSeter 本身不执行,所有方法都延迟到第一次调用 QueryRow/Exec 等才发请求;而 SetArgs() 只是复用同一个 RawSeter 实例换参,并不改变其惰性行为。别指望链式调用 Raw(...).SetArgs(...).QueryRow(...) 里中间步骤有副作用——它只是构造器模式而已。











