至此,原生SQL的操作方式就已经完成了,并且可以通过kdb.WithDB("mysql::master")这样的方式来选择指定的DB 。接下来我们来实现查询结果转换 。
第三步: 实现三种返回结果
查询结果并转换成我们想要的结构,从本质上来说,首先,我们通过rows.Columns得到查询语句中会返回的字段都有哪些,知道有哪些字段以后,我们就可以知道需要 传入到rows.Scan中需要几个参数,我们先来看看ToArray()的实现
func (r *Rows) ToArray() (data [][]string, err error) { if r.rs == nil { return nil, r.lastError } defer r.rs.Close() //获取查询的字段 fields, err := r.rs.Columns() if err != nil { r.lastError = err return nil, err } data = https://www.isolves.com/it/cxkf/yy/go/2021-03-03/make([][]string, 0) num := len(fields) //根据查询字段的数量,生成[num]interface{}用于存储Scan的结果 refs := make([]interface{}, num) for i := 0; i < num; i++ { var ref interface{} refs[i] = &ref } for r.rs.Next() { result := make([]string, len(fields)) if err := r.rs.Scan(refs...); err != nil { return nil, err } for i := range fields { //把*interface{}转换成strings返回 if val, err := toString(refs[i]); err == nil { result[i] = val } else { return nil, err } } if err != nil { r.lastError = err return nil, err } data = append(data, result) } return data, nil}我们再增加一个convert.go文件,实现toString()的方法
package kdbimport ( "fmt" "reflect" "strconv" "time")//转换成stringfunc toString(src interface{}) (dst string, err error) { inf := reflect.Indirect(reflect.ValueOf(src)).Interface() if inf == nil { return "", nil } switch v := inf.(type) { case string: dst = v return case []byte: dst = string(v) return } val := reflect.ValueOf(inf) typ := reflect.TypeOf(inf) switch typ.Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: dst = strconv.FormatInt(val.Int(), 10) case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: dst = strconv.FormatUint(val.Uint(), 10) case reflect.Float32, reflect.Float64: dst = strconv.FormatFloat(val.Float(), 'f', -1, 64) case reflect.Bool: dst = strconv.FormatBool(val.Bool()) case reflect.Complex64, reflect.Complex128: dst = fmt.Sprintf("%v", val.Complex()) case reflect.Struct: //time.Time var timeType time.Time if typ.ConvertibleTo(reflect.TypeOf(timeType)) { dst = val.Convert(reflect.TypeOf(timeType)).Interface().(time.Time).Format(time.RFC3339Nano) } else { err = fmt.Errorf("unsupported struct type %v", val.Type()) } default: err = fmt.Errorf("unsupported struct type %v", val.Type()) } return}再增加toMap的实现,原理跟toArray的一样的,
func (r *Rows) ToMap() (data []map[string]string, err error) { if r.rs == nil { return nil, r.lastError } defer r.rs.Close() fields, err := r.rs.Columns() if err != nil { r.lastError = err return nil, err } data = https://www.isolves.com/it/cxkf/yy/go/2021-03-03/make([]map[string]string, 0) num := len(fields) result := make(map[string]string) refs := make([]interface{}, num) for i := 0; i < num; i++ { var ref interface{} refs[i] = &ref } for r.rs.Next() { if err := r.rs.Scan(refs...); err != nil { return nil, err } for i, field := range fields { if val, err := toString(refs[i]); err == nil { result[field] = val } else { return nil, err } } data = append(data, result) } return data, nil}接下来我们要实现toStruct方法,再实现toStruct之前,我们先来梳理一下逻辑 。1.传入参数必须是指针 2.传入的类型必须是slice 3.传入的slice类型必须的struct 4.提取struct中tag,并过滤出指定的tag,假设我们定义的tag名为db,则只提取tag为db的内容,例如
type user struct { Id int `db:"id"` Name string `db:"name"` Sex int}我们提取的内容为id,name 5.遍历查询的字段,再tag中查找是否存在,如果存在则使用结构体成员的变量地址进行Scan 。
func (r *Rows) ToStruct(st interface{}) error { //st->&[]user //获取变量的类型,类型为指针 stType := reflect.TypeOf(st) //获取变量的值 stVal := reflect.ValueOf(st) stValInd := reflect.Indirect(stVal) //1.参数必须是指针 if stType.Kind() != reflect.Ptr { return fmt.Errorf("the variable type is %v, not a pointer", stType.Kind()) } //指针指向的类型:slice stTypeInd := stType.Elem() //2.传入的类型必须是slice,slice的成员类型必须是struct if stTypeInd.Kind() != reflect.Slice || stTypeInd.Elem().Kind() != reflect.Struct { return fmt.Errorf("the variable type is %v, not a slice struct", stType.Elem().Kind()) } if r.Rs == nil { return r.LastError } defer r.Rs.Close() //初始化struct v := reflect.New(stTypeInd.Elem()) //提取结构体中的tag tagList, err := extractTagInfo(v) if err != nil { return err } fields, err := r.Rs.Columns() if err != nil { r.LastError = err return err } refs := make([]interface{}, len(fields)) for i, field := range fields { //如果对应的字段在结构体中有映射,则使用结构体成员变量的地址 if f, ok := tagList[field]; ok { refs[i] = f.Addr().Interface() } else { refs[i] = new(interface{}) } } for r.Rs.Next() { if err := r.Rs.Scan(refs...); err != nil { return err } stValInd = reflect.Append(stValInd, v.Elem()) } stVal.Elem().Set(stValInd) return nil}
推荐阅读
- 使用Redis轻松实现秒杀系统
- 使用Arkime抓包,捕获解密HTTPS流量
- 梦境是不是另一个平行世界 做梦是不是平行时空的记忆
- 投影仪灯泡寿命是多久 如何延长投影机灯泡使用寿命
- datedif函数的使用方法?DATEDIF函数_1
- 测试人员如何使用Git部署测试环境?
- 数据库中的索引,原理是什么?为什么查询使用索引就会快?
- 为什么使用移动路由的人,往往不愿意使用手机热点?
- raise3d打印机使用教程?raise3d打印机中文说明书
- 打造好运连连的客厅风水