如何将扁平化查询结果映射到含数组的嵌套Go结构体?
问题:将扁平化数据库记录映射到Go嵌套结构体
我定义了如下Go结构体:
type Product struct { ID int `json:"id"` Name string `json:"name"` Dimensions []Dimensions `json:"dimensions"` } type Dimensions struct { Length int `json:"length"` Width int `json:"width"` Height int `json:"height"` }
数据以扁平化形式存储,每组Dimensions对应重复的ID和Name(ID、Name与Dimensions是一对多关系),例如ID为1、名称为ABC的产品对应两组尺寸:
| ID | Name | Length | Width | Height |
|---|---|---|---|---|
| 1 | ABC | 1 | 2 | 3 |
| 1 | ABC | 4 | 5 | 6 |
请问如何通过StructScan或其他方法,将数据正确映射到嵌套结构体中,使Dimensions数组正确嵌套?
解决方案
直接用StructScan无法自动处理一对多的嵌套映射——它会把每一行记录单独映射为一个结构体实例,导致重复生成Product对象。我们需要先查询所有扁平化数据,再通过分组逻辑合并嵌套的Dimensions数组,以下是两种可行方案:
方法1:临时结构体+分组合并(最直接高效)
- 定义与数据库表字段一一对应的临时结构体:
type FlatProduct struct { ID int `db:"id"` Name string `db:"name"` Length int `db:"length"` Width int `db:"width"` Height int `db:"height"` }
- 查询所有数据到临时结构体切片:
var flatProducts []FlatProduct err := db.Select(&flatProducts, "SELECT id, name, length, width, height FROM products WHERE id = ?", 1) if err != nil { // 此处处理查询错误 }
- 用map按ID分组,合并同ID产品的Dimensions:
productMap := make(map[int]*Product) for _, fp := range flatProducts { if p, exists := productMap[fp.ID]; exists { // 产品已存在,追加新尺寸 p.Dimensions = append(p.Dimensions, Dimensions{ Length: fp.Length, Width: fp.Width, Height: fp.Height, }) } else { // 首次遇到该产品,初始化结构体 productMap[fp.ID] = &Product{ ID: fp.ID, Name: fp.Name, Dimensions: []Dimensions{ { Length: fp.Length, Width: fp.Width, Height: fp.Height, }, }, } } } // 若需要切片格式,转换map值为切片 var products []Product for _, p := range productMap { products = append(products, *p) }
方法2:自定义扫描逻辑(适用于sqlx库)
如果你使用sqlx,可以实现sql.Scanner接口处理嵌套,但本质仍需配合分组逻辑去重,适合需要复用扫描逻辑的场景:
func (p *Product) Scan(src interface{}) error { row, ok := src.([]interface{}) if !ok { return fmt.Errorf("expected []interface{}, got %T", src) } // 解析数据库返回的字段值 id, _ := row[0].(int64) name, _ := row[1].(string) length, _ := row[2].(int64) width, _ := row[3].(int64) height, _ := row[4].(int64) p.ID = int(id) p.Name = name p.Dimensions = append(p.Dimensions, Dimensions{ Length: int(length), Width: int(width), Height: int(height), }) return nil }
使用该自定义扫描时,仍需在查询后用map对结果去重合并,否则会生成多个同ID的Product实例。
内容的提问来源于stack exchange,提问作者user3277752
相关产品推荐
相关产品推荐

