golang mysql select * 优化

栏目: 数据库 · Mysql · 发布时间: 6年前

内容简介:初学者使用golang mysql做查询时,一般直接使用原生的select * from table来查询数据:后来数据表结构变动直接导致GetUserInfo()报错了,因为字段对应不上了。然后优化了一版:此时问题解决了,然后继续写类似的代码。

初学者使用golang mysql做查询时,一般直接使用原生的select * from table来查询数据:

type UserInfo struct {
    Id   int64
    Name string
    Sex  string
    //...
}

func GetUserInfo(uid int64) (info UserInfo, err error) {
    Db := lib.MysqlConn()
    sql := fmt.Sprintf("select * from user where uid=%d", uid)
    rows, err := Db.Query(sql)
    if err != nil {
        return info, err
    }
    defer Db.Close()

    if rows.Next() {
        var (
            id   int64
            name string
            sex  string
            //...
        )
        //得一个个的字段罗列出来
        err = rows.Scan(&id, &name, &sex, //...)
        info = UserInfo{
            Id:   id,
            Name: name,
            Sex:  sex,
            //...
        }
    }

    return info, nil
}

后来数据表结构变动直接导致GetUserInfo()报错了,因为字段对应不上了。然后优化了一版:

func GetUserInfo(uid int64) (info UserInfo, err error) {
    Db := lib.MysqlConn()
    sql := fmt.Sprintf("select `id`,`user_name`,`sex`,... from user where uid=%d", uid)
    rows, err := Db.Query(sql)
    if err != nil {
        return
    }
    defer Db.Close()

    if rows.Next() {
        //得一个个的字段罗列出来
        err = rows.Scan(&info.Id, &info.Name, &info.Sex, //...)
        if err != nil {
            return
        }
    }

    return
}

此时问题解决了,然后继续写类似的代码。

时间久了,就发现这样写好累,每次都得把所有的字段都罗列出来,而且上下一一对应。

所以就想能不能自动生成上下对应的变量,然后直接用变量来替换上。

如是,就用到了golang的反射,先定义一个结构体和数据表的字段一一对应,并在字段说明里面定义数据库的字段名:

type UserInfo struct {
	Id   int64  `sql:"id"`
	Name string `sql:"user_name"`
	Sex  string `sql:"sex"`
	//...
}

可以使用反射提取出类似`sql:"id"`里面id,然后拼装起来,具体如下

func (ui *UserInfo) allFields() (sqlFields string) {
	arr := []string{}
	el := reflect.TypeOf(ui).Elem()
	for i := 0; i < el.NumField(); i++ {
		arr = append(arr, el.Field(i).Tag.Get("sql"))
	}

	sqlFields = "`" + strings.Join(arr, "`,`") + "`"
	return
}

所以上面的函数中 sql 语句的定义就改为了:

//...
sql := fmt.Sprintf("select "+info.allFields()+" from user where uid=%d", uid)
//...

问题又来了,

rows.Scan(&info.Id, &info.Name, &info.Sex, //...)

rows.Scan怎么优化?

这里实际上就是每个字段的地址,如果取出该变量的地址,组成一个切片,然后展开切片就可以了,再来定义方法:

func (ui *UserInfo) allValues() (sqlValues []interface{}) {
	vl := reflect.ValueOf(ui).Elem()
	num := reflect.TypeOf(ui).Elem().NumField()
	for i := 0; i < num; i++ {
		sqlValues = append(sqlValues, vl.Field(i).Addr().Interface())
	}
	return
}

func GetUserInfo(uid int64) (info UserInfo, err error) {
	Db := lib.MysqlConn()
	sql := fmt.Sprintf("select "+info.allFields()+" from user where uid=%d", uid)
	rows, err := Db.Query(sql)
	if err != nil {
		return
	}
	defer Db.Close()

	if rows.Next() {
		//获取每个字段的切片
		fields := info.allValues()
		err = rows.Scan(fields...)
		if err != nil {
			return
		}
	}

	return
}

把 info.allFields()和info.allValues()合并在一起返回:

type UserInfo struct {
	Id   int64  `sql:"id"`
	Name string `sql:"user_name"`
	Sex  string `sql:"sex"`
	//...
}

func (ui *UserInfo) allFieldsAndValues() (sqlFields string, sqlValues []interface{}) {
	arr := []string{}
	el := reflect.TypeOf(ui).Elem()
	vl := reflect.ValueOf(ui).Elem()
	for i := 0; i < el.NumField(); i++ {
		arr = append(arr, el.Field(i).Tag.Get("sql"))
		sqlValues = append(sqlValues, vl.Field(i).Addr().Interface())
	}

	sqlFields = "`" + strings.Join(arr, "`,`") + "`"

	return
}

func GetUserInfo(uid int64) (info UserInfo, err error) {
	Db := lib.MysqlConn()

	sqlFields, sqlValues := info.allFieldsAndValues()
	sql := fmt.Sprintf("select "+sqlFields+" from user where uid=%d", uid)
	rows, err := Db.Query(sql)
	if err != nil {
		return
	}
	defer Db.Close()

	if rows.Next() {
		err = rows.Scan(sqlValues...)
	}

	return
}

现在封装起来后就方便多了,不担心数据表结构变动:这里只需要修改结构体UserInfo的信息。

现在又有一个方法需要批量的获取UserInfo,方法UserInfo.allFieldsAndValues()就大有用处了:

func GetAllUserInfo() (all []UserInfo, err error) {
	Db := lib.MysqlConn()
	info := UserInfo{}
	sqlFields, sqlValues := info.allFieldsAndValues()
	rows, err := Db.Query("select " + sqlFields + " from user ")
	if err != nil {
		return
	}
	defer Db.Close()

	for rows.Next() {
		err = rows.Scan(sqlValues...)
		if err != nil {
			return
		}
		all = append(all, info)
		info = UserInfo{}
	}

	return
}

这里在循环内[for rows.Next(){//...}]因为结构体的传值是值传递,all = append(all, info)操作的是info的一个拷贝。

用完之后在把变量info的值给重置掉:info = UserInfo{}。最后返回all。

Go语言中有的传参是值传递(传值),是一个副本,一个拷贝。

因为拷贝的内容有时候是非引用类型(int、string、struct等这些),这样就在函数中就无法修改原内容数据;

有的是引用类型(指针、map、slice、chan等这些),这样就可以修改原内容数据

文章来源于 https://www.lixiaocheng.com/read/703


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

七周七语言(卷2)

七周七语言(卷2)

【美】Bruce A. Tate(泰特)、Fred Daoud(达乌德)、Ian Dees(迪斯) / 7ML翻译组 / 人民邮电出版社 / 2016-12 / 59

深入研习对未来编程具有重要意义的7种语言 Lua、Factor、Elixir、Elm、Julia、Idris和MiniKanren 本书带领读者认识和学习7种编程语言,旨在帮助读者探索更为强大的编程工具。 本书延续了同系列的畅销书《七周七语言》《七周七数据库》和《七周七Web开发框架》的体例和风格。 全书共8章,前7章介绍了Lua、Factor、Elm、Elixir、Jul......一起来看看 《七周七语言(卷2)》 这本书的介绍吧!

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具