kaisawind's blog
  • 关于
  • 所有帖子

go mysql数据转换 - Wed, Sep 8, 2021

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
}


辽ICP备2021007608号 | © 2025 | kaisawind

Facebook Twitter GitHub