
本文介绍如何利用 go 的反射机制,根据任意结构体类型动态创建其实例并扫描 sql 查询结果,避免硬编码具体结构体,实现真正的泛型化数据映射。
本文介绍如何利用 go 的反射机制,根据任意结构体类型动态创建其实例并扫描 sql 查询结果,避免硬编码具体结构体,实现真正的泛型化数据映射。
在 Go 中处理动态 SQL 查询结果时,常面临一个核心挑战:如何不依赖具体结构体(如 User)而通用地将 *sql.Rows 映射为一组结构体实例。Go 本身不支持泛型(在 Go 1.18 前),但可通过 reflect 包在运行时完成类型推导与实例构造,从而实现“类型驱动”的动态解码。
关键在于两点:
- 动态创建结构体指针:使用 reflect.New(reflect.TypeOf(u)) 获取指向新零值实例的指针;
- 统一生成扫描参数切片:通过 reflect.Value.Elem() 访问字段地址,构造 []interface{} 供 rows.Scan() 使用。
以下是完整、健壮的实现示例:
import (
"database/sql"
"encoding/json"
"fmt"
"reflect"
)
// StrutForScan 接收结构体指针,返回各字段地址构成的 interface{} 切片
func StrutForScan(u interface{}) []interface{} {
val := reflect.ValueOf(u).Elem() // 确保 u 是 *T 类型
if val.Kind() != reflect.Struct {
panic("StrutForScan: argument must be a pointer to struct")
}
v := make([]interface{}, val.NumField())
for i := 0; i <p>⚠️ <strong>重要注意事项</strong>: </p>
- 传入 StructRow 的 sample 参数仅用于类型推导,可以是零值(User{})或 &User{},函数内部会自动处理指针层级;
- StrutForScan 要求输入必须是指向结构体的指针(*T),否则 .Elem() 将 panic;
- 字段必须可寻址(即不能是嵌入的不可寻址字段或未导出字段),且数据库列顺序必须严格匹配结构体字段声明顺序(按 tag 无法自动对齐,需手动保证);
- 生产环境建议配合 sqlx 或 gorp 等成熟 ORM/Query 工具,它们已内置更安全、高效的反射映射逻辑。
该方案在无泛型时代提供了简洁可控的动态映射能力,是理解 Go 反射与 SQL 交互的经典实践。











