[Golang]OpenGauss数据库简易orm搭建(二)单数据查询

这是我参与11月更文挑战的第26天,活动详情查看:2021最后一次更文挑战

前言

上文中,我们实现了通过go语言结构体在OpenGauss数据库上实现自动建表,在本文中,将在上文结构体的基础上,实现单条数据的查询操作。

生成sql语句

查询的基础是构建一条正确的sql语句,对于查询操作,我们直接使用SELECT *来获取行各字段,我们需要构建下面一条语句:

SELECT * FROM table WHERE id = 1;
复制代码

可以看出,我们需要实现对所有表进行查询操作,需要两个变量:

  1. 查询表名称
  2. 约束条件

对于表名,我们直接使用reflect.typeOf(model)反射出表名:

   t := reflect.TypeOf(model)
   tableName := strings.Split(t.String(), ".")[1]
复制代码

对于约束条件来说,作为参数直接输出即可,完成代码如下:

// First 按字段名查询单个
// odds => WHERE id = 1 || LIKE name = '%cj%' || ......
func First(model interface{}, odds string) error {
   // 反射获取表名
   t := reflect.TypeOf(model)
   tableName := strings.Split(t.String(), ".")[1]
   // 构建查询语句并交付数据库查询
   sql := "SELECT * FROM " + tableName + " " + odds
   return getFirst(model, sql)
}
复制代码

读取返回数据

上面构建sql语句还是挺容易的,但是要实现返回的将赋值给输出的model对象就难了,下面分几个部分进行实现:

一、将sql语句交付数据库查询

   logrus.Debugln(sql)
   rows, err := db.Query(sql)
   if err != nil {
      return err
   }
   defer rows.Close()
复制代码

首先,我们先使用db.Query(sql),将sql交付给openGauss数据库,然后返回一个rows对象,此时,如果没有报错的话,数据库返回的数据会被存储在rows内。

二、存储返回数据

想要读取rows中的数据,我们需要先执行rows.Next(),该函数会返回一个bool类型数据,如果可以读取到数据,则会返回true,否则返回false。

另外,换行操作也封装在rows.Next()中,因此在读取行之前,需要先执行一次rows.Next(),然后才能读到数据。

构建代码如下:

   if !rows.Next() {
      return errors.New("sql: Scan called without calling Next")
   }
复制代码

这里我们只读首行,多行读取可以通过for循环进行,但是在赋值方面差异较大,我们后面再讨论。

然后我们在需要读取出rows中的数据之前,需要先读取rows.Columns,即行的字段,通过len()读取出字段数量,然后用该长度构建一个slice:

   columns, err := rows.Columns()
   if err != nil {
      return err
   }
复制代码

至于为什么要该数据,是因为我们需要使用rows.Scan()来读取数据,Scan函数参数如下:

image.png

可以看出需要传入的是可变参数,而传入的参数数量需要与SELECT返回回来的个数相同,否则会报错,因此我们使用slice的一个特性,使用...也传一个可变的入参。

代码实现如下,由于Scan输入为指针类型,需要在输入前对values进行初始化:

   values := make([]interface{}, len(columns))
   for i := range values {
      values[i] = new(string)
   }
   if err := rows.Scan(values...); err != nil {
      return err
   }
复制代码

然后,然后返回值就会保存在values数值中了。

三、构建map,转移数据

由于在slice中的数据是没有字段数据的,因此我们需要使用上面的columns和values共同构建一个map,用于存储读取到的数据,详情见代码即可。

代码实现如下:

   // 4、构建map,将数组数据转移到map缓存中
   m := make(map[string]interface{})
   for i, column := range columns {
      m[strings.ToUpper(column[:1]) + column[1:]] = *values[i].(*string)
   }
复制代码

四、反射数据到原对象

然后就是查询操作中最重要的一个环节:将数据反射到原对象中。

这里使用的是反射中的reflect.ValueOf()函数,获得原对象的Value类型数据,然后通过其下的.Elem().FieldByName函数,通过字段名获取到我们需要修改字段的值位置,最后使用SetString()函数实现赋值操作。而所使用的字段名和之前一样,通过reflect.TypeOf()函数,然后遍历获取。

代码实现如下:

func getFirst(model interface{}, sql string) error {
   // 5、使用反射将map映射到原结构体中
   t := reflect.TypeOf(model)
   for i := 0; i < t.Elem().NumField(); i++ {
      field := t.Elem().Field(i)
      if m[field.Name] != nil {
         v := reflect.ValueOf(model).Elem().FieldByName(field.Name)
         v.SetString(m[field.Name].(string))
      }
   }
   return nil
}
复制代码

调用函数

构建一个空结构体,然后调用函数对齐赋值:

r := &models.Role{}
err := models.First(r, "WHERE id = 1")
if err != nil {
   fmt.Println(err)
}
fmt.Println(r)
复制代码

输出如下:

image.png

猜你喜欢

转载自juejin.im/post/7035623180110659615