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
}