在 Go 中,通过反射来动态判断并处理不同类型的结构体是一种常见的做法。虽然在具体场景中,需要根据实际情况判断结构体类型,但你可以通过使用类型注册或者工厂模式来简化代码。
下面演示了使用类型注册的方式:
package main
import (
"fmt"
"log"
"reflect"
"github.com/jmoiron/sqlx"
_ "github.com/go-sql-driver/mysql"
)
// 定义示例的结构体类型
type Person struct {
ID int `db:"id"`
Name string `db:"name"`
Age int `db:"age"`
}
type Car struct {
ID int `db:"id"`
Model string `db:"model"`
Year int `db:"year"`
}
// 定义一个通用的接口
type Record interface{}
// 注册结构体类型
var registeredTypes = map[string]reflect.Type{
"Person": reflect.TypeOf(Person{}),
"Car": reflect.TypeOf(Car{}),
}
func main() {
// 创建数据库连接
db, err := sqlx.Connect("mysql", "username:password@tcp(127.0.0.1:3306)/dbname")
if err != nil {
log.Fatal(err)
}
defer db.Close()
// 执行查询语句
rows, err := db.Queryx("SELECT id, name, age FROM people")
if err != nil {
log.Fatal(err)
}
defer rows.Close()
// 定义一个切片用于存储查询结果
var records []Record
// 获取查询结果的列信息
columns, err := rows.Columns()
if err != nil {
log.Fatal(err)
}
// 遍历查询结果,并动态创建对应结构体类型的实例
for rows.Next() {
// 获取每一行的值
values := make([]interface{}, len(columns))
for i := range values {
values[i] = new(interface{})
}
// 将每一行的值存储到 values 中
if err := rows.Scan(values...); err != nil {
log.Fatal(err)
}
// 根据列信息确定使用哪种结构体类型
recordType, ok := registeredTypes[columns[0]]
if !ok {
log.Fatal("Unknown record type")
}
// 动态创建对应结构体类型的实例
record := reflect.New(recordType).Elem()
// 将每一行的值映射到结构体实例中
for i, columnName := range columns {
field := record.FieldByName(columnName)
if field.IsValid() {
field.Set(reflect.ValueOf(*(values[i].(*interface{}))))
}
}
// 将结果添加到切片中
records = append(records, record.Interface())
}
// 输出查询结果
fmt.Printf("%+v\n", records)
}
在这个例子中,我使用 registeredTypes
来注册不同的结构体类型,根据列信息来动态选择使用哪种类型。这样你只需要在程序中注册需要的结构体类型,而不必在主逻辑中进行硬编码的条件判断。这使得代码更加灵活和可维护。