面试题答案
一键面试在构建通用的数据库查询结果解析模块时,Go反射第一定律的应用场景在于,数据库查询结果通常以接口类型返回,我们需要将其转换为反射对象,以便动态地操作数据结构。
以下是一个简单的代码示例:
package main
import (
"database/sql"
"fmt"
"reflect"
_ "github.com/go-sql-driver/mysql"
)
// User 定义用户结构体
type User struct {
ID int
Name string
}
// ScanRows 通用的数据库查询结果解析函数
func ScanRows(rows *sql.Rows, target interface{}) error {
// 获取目标值的反射对象
valueOf := reflect.ValueOf(target)
// 确保目标是一个指针且是可设置的
if valueOf.Kind() != reflect.Ptr || valueOf.IsNil() {
return fmt.Errorf("target must be a non-nil pointer")
}
valueOf = valueOf.Elem()
if valueOf.Kind() != reflect.Struct {
return fmt.Errorf("target must be a struct pointer")
}
// 获取结构体字段数量
numFields := valueOf.NumField()
// 创建一个切片来存储每列的值
columns := make([]interface{}, numFields)
for i := 0; i < numFields; i++ {
columns[i] = valueOf.Field(i).Addr().Interface()
}
// 循环扫描每一行
for rows.Next() {
err := rows.Scan(columns...)
if err != nil {
return err
}
}
return nil
}
func main() {
db, err := sql.Open("mysql", "user:password@tcp(127.0.0.1:3306)/test")
if err != nil {
panic(err.Error())
}
defer db.Close()
rows, err := db.Query("SELECT id, name FROM users LIMIT 1")
if err != nil {
panic(err.Error())
}
defer rows.Close()
var user User
err = ScanRows(rows, &user)
if err != nil {
panic(err.Error())
}
fmt.Printf("User: ID=%d, Name=%s\n", user.ID, user.Name)
}
在上述代码中:
ScanRows
函数接受一个sql.Rows
对象和一个目标接口类型的指针。- 通过
reflect.ValueOf(target)
获取目标值的反射对象,并检查它是否是一个非空指针且指向一个结构体。 - 创建一个
columns
切片,使用反射获取结构体每个字段的地址,并将其存储在切片中。 - 使用
rows.Scan
方法将查询结果填充到columns
切片,进而填充到结构体中。
这样就利用了Go反射第一定律,将接口值(数据库查询结果的接口类型)转换为反射对象,实现了通用的数据库查询结果解析。