使用Golang反射从数据库加载数据项

使用Golang反射从数据库加载数据项 我正在使用数据库,并尝试创建用于处理结构的通用函数。
我已经实现了 Create() 和 Insert(),并希望实现具有相同布局的 Select():
函数名 (表名, 结构体)… 例如:

handler.Create("cars", car{})
handler.Insert("cars", car{color:"blue"})

请在 Google Playground 上查看实现。

现在,我想对 Select() 做同样的事情,但在指针检索方面遇到了困难。

func (d *databaseHandler) Select(table string, row interface{}) ([]interface{}, error)

请在 Google Playground 上查看当前函数代码。

有人可以给我指点正确的方向吗?
谢谢!


更多关于使用Golang反射从数据库加载数据项的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于使用Golang反射从数据库加载数据项的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


根据你的代码,问题在于 Select 函数需要处理指针类型的结构体,以便能够通过反射设置字段值。以下是修改后的实现,使用反射从数据库加载数据到结构体指针切片中:

package main

import (
    "database/sql"
    "fmt"
    "reflect"
)

type databaseHandler struct {
    db *sql.DB
}

func (d *databaseHandler) Select(table string, row interface{}) ([]interface{}, error) {
    // 获取 row 的类型信息
    rowType := reflect.TypeOf(row)
    if rowType.Kind() != reflect.Ptr {
        return nil, fmt.Errorf("row must be a pointer to struct")
    }
    
    elemType := rowType.Elem()
    if elemType.Kind() != reflect.Struct {
        return nil, fmt.Errorf("row must be a pointer to struct")
    }

    // 模拟数据库查询结果
    rows := []map[string]interface{}{
        {"color": "blue", "model": "sedan"},
        {"color": "red", "model": "coupe"},
    }

    var results []interface{}

    for _, dbRow := range rows {
        // 创建新的结构体实例
        newRow := reflect.New(elemType).Elem()
        
        // 遍历结构体字段
        for i := 0; i < elemType.NumField(); i++ {
            field := elemType.Field(i)
            fieldName := field.Name
            
            // 从数据库行中获取对应字段的值
            if dbValue, exists := dbRow[fieldName]; exists {
                fieldValue := newRow.Field(i)
                
                if fieldValue.CanSet() {
                    // 根据字段类型设置值
                    dbVal := reflect.ValueOf(dbValue)
                    if dbVal.Type().ConvertibleTo(fieldValue.Type()) {
                        fieldValue.Set(dbVal.Convert(fieldValue.Type()))
                    }
                }
            }
        }
        
        // 将填充好的结构体添加到结果中
        results = append(results, newRow.Addr().Interface())
    }

    return results, nil
}

// 示例用法
type car struct {
    Color string
    Model string
}

func main() {
    handler := &databaseHandler{}
    
    // 注意:传入指向结构体的指针
    results, err := handler.Select("cars", &car{})
    if err != nil {
        panic(err)
    }
    
    for i, result := range results {
        if carPtr, ok := result.(*car); ok {
            fmt.Printf("Result %d: Color=%s, Model=%s\n", i, carPtr.Color, carPtr.Model)
        }
    }
}

关键修改点:

  1. 参数验证:确保传入的 row 参数是指向结构体的指针
  2. 动态创建实例:使用 reflect.New(elemType).Elem() 创建新的结构体实例
  3. 字段映射:通过结构体字段名匹配数据库列名
  4. 类型转换:使用 Convert() 方法处理不同类型之间的转换
  5. 返回指针:使用 Addr().Interface() 返回结构体指针

示例输出:

Result 0: Color=blue, Model=sedan
Result 1: Color=red, Model=coupe

这个实现能够正确处理指针类型,并将数据库查询结果填充到对应的结构体字段中。你可以根据实际的数据库驱动和查询逻辑调整数据库访问部分。

回到顶部