如何实现database/sql.Scanner接口适配数据库查询字段?
实现
database/sql.Scanner接口的正确方式 问题根源
你已经正确实现了database/sql.Scanner接口,但调用代码存在两个核心错误:
cols切片里存的是类型本身(Type_int、Type_string),而非这些类型的实例指针——rows.Scan需要接收实现了Scanner接口的类型指针,才能触发Scan方法修改实例值。- 后续对
ptr[i]做*any类型断言完全错误,因为cols[i]的实际类型是你定义的自定义类型,不是any。
修复后的完整代码
自定义类型实现(无需修改)
import "fmt" type Type_int int func (t *Type_int) Scan(value interface{}) error { switch value := value.(type) { case int64: *t = Type_int(value) default: return fmt.Errorf("Invalid database type: %T %v", value, value) } return nil } type Type_string string func (t *Type_string) Scan(value interface{}) error { switch value := value.(type) { case []uint8: *t = Type_string(value) default: return fmt.Errorf("Invalid database type: %T %v", value, value) } return nil }
静态列场景的调用代码
如果列数量和类型固定,直接创建实例指针传入即可:
// 初始化自定义类型实例 var id Type_int var isSuspended Type_int var name Type_string // 准备Scanner接口指针切片 ptr := []interface{}{&id, &isSuspended, &name} if err := rows.Scan(ptr...); err != nil { fmt.Println("err:", err) return } // 映射列名到结果 res := make(map[string]any) res_cols := []string{"id", "is_suspended", "name"} // 假设这是查询返回的列名列表 res[res_cols[0]] = id res[res_cols[1]] = isSuspended res[res_cols[2]] = name // 打印结果 for k, v := range res { fmt.Printf("Type: %T %s\n", v, k) }
动态列场景的调用代码
如果列数量或类型不固定,用反射动态生成实例指针:
import "reflect" // 定义列对应的类型列表 colTypes := []reflect.Type{ reflect.TypeOf(Type_int(0)), reflect.TypeOf(Type_int(0)), reflect.TypeOf(Type_string("")), } ptr := make([]interface{}, len(colTypes)) for i, t := range colTypes { // 创建类型零值实例并取地址 ptr[i] = reflect.New(t).Interface() } if err := rows.Scan(ptr...); err != nil { fmt.Println("err:", err) return } // 提取结果到map res := make(map[string]any) for i, name := range res_cols { // 获取实例的实际值 res[name] = reflect.ValueOf(ptr[i]).Elem().Interface() fmt.Printf("Type: %T %s\n", res[name], name) }
内容的提问来源于stack exchange,提问作者clarkk
相关产品推荐
相关产品推荐

