go mysql数据转换

  1. 1. 概述
  2. 2. 查询
  3. 3. 插入
  4. 4. 转结构体

go mysql数据转换

1. 概述

go语言结构体与mysql之间转换。
整体思路:使用template和reflect进行CRUD;使用reflect将rows转换为结构体。

2. 查询

查询最简单

go模板文件

SELECT * FROM {{.table}}
{{- if .query}} WHERE {{.query}} {{end -}}
{{- if .limit -}}
    {{- if ne .limit 0 -}}LIMIT {{.limit}} {{end -}}
{{- end -}}
{{- if .skip -}}
    {{- if ne .skip 0 -}}OFFSET {{.skip}} {{end -}}
{{- end -}}
;

Go代码

// SQLQuery query sql string
func SQLQuery(table string, limit, skip int64, sort, query string) (sql string) {
    params := map[string]interface{}{
        "table": table,
        "limit": limit,
        "skip":  skip,
        "query": query,
        "sort":  sort,
    }
    tpl, err := template.New("query").Parse(Query)
    if err != nil {
        return
    }
    buf := &bytes.Buffer{}
    err = tpl.Execute(buf, params)
    if err != nil {
        return
    }
    sql = buf.String()
    return
}

3. 插入

使用反射取json的tag作为key,取值作为值。使用insert语句,将key和value一一对应。
需要注意:字符串需要加单引号,数字不需要。将map转换为字符串填充。

go模板文件

INSERT INTO {{.table}}({{.keys}}) VALUES({{.values}});

Go代码

// SQLInsert insert sql string
func SQLInsert(table string, in proto.Message) (sql string) {
    refValue := reflect.ValueOf(in)
    if refValue.Kind() == reflect.Ptr {
        refValue = refValue.Elem()
    }
    refType := refValue.Type()
    params := map[string]interface{}{
        "table": table,
    }
    tmp := map[string]string{}
    for i := 0; i < refType.NumField(); i++ {
        ft := refType.Field(i)
        if !ft.IsExported() {
            continue
        }
        fv := refValue.FieldByName(ft.Name)
        tag := ft.Tag.Get("json")
        switch fv.Kind() {
        case reflect.Map:
            v, _ := json.Marshal(fv.Interface())
            tmp[tag] = fmt.Sprintf("'%s'", v)
        case reflect.String:
            tmp[tag] = fmt.Sprintf("'%v'", fv.Interface())
        case reflect.Int32:
            // Note: Sprintf 会调用String函数
            mf := fv.MethodByName("String")
            if mf.IsValid() {
                tmp[tag] = fmt.Sprintf("'%v'", fv.Interface())
            }
        default:
            // Note: Sprintf 会调用String函数
            mf := fv.MethodByName("String")
            if mf.IsValid() {
                tmp[tag] = fmt.Sprintf("'%v'", fv.Interface())
            } else {
                if tag == "created_at" || tag == "updated_at" {
                    tmp[tag] = "now()"
                } else {
                    tmp[tag] = fmt.Sprintf("%v", fv.Interface())
                }
            }
        }
        // logrus.Infoln(fv.Kind().String(), ft.Name, fv.Interface())
    }
    // logrus.Infoln(tmp)
    var keys []string
    var values []string
    for k, v := range tmp {
        keys = append(keys, k)
        values = append(values, v)
    }
    params["keys"] = strings.Join(keys, ",")
    params["values"] = strings.Join(values, ",")
    tpl, err := template.New("insert").Parse(Insert)
    if err != nil {
        return
    }
    buf := &bytes.Buffer{}
    err = tpl.Execute(buf, params)
    if err != nil {
        return
    }
    sql = buf.String()
    return
}

4. 转结构体

将scan出的列与结构体反射出的json tag一一对应,通过fv.Addr().Interface()提取字段指针,使用rows.Scan直接对反射出的指针赋值。

// Scan scan rows to message
func Scan(rows *sql.Rows, in proto.Message) (err error) {
    refValue := reflect.ValueOf(in)
    if refValue.Kind() == reflect.Ptr {
        refValue = refValue.Elem()
    }
    cols, err := rows.Columns()
    if err != nil {
        return
    }
    refType := refValue.Type()
    columns := make([]interface{}, len(cols))
    for i := 0; i < refValue.NumField(); i++ {
        ft := refType.Field(i)
        if !ft.IsExported() {
            continue
        }
        fv := refValue.FieldByName(ft.Name)
        tag := ft.Tag.Get("json")
        for j, col := range cols {
            if col == tag {
                columns[j] = fv.Addr().Interface()
            }
        }
    }
    err = rows.Scan(columns...)
    if err != nil {
        logrus.WithError(err).Errorln("rows scan error")
        return
    }
    logrus.Infoln(in)
    return
}

转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 wind.kaisa@gmail.com

💰

×

Help us with donation